Skip to main content

zerocopy/
byteorder.rs

1// SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0 OR MIT
2//
3// Copyright 2019 The Fuchsia Authors
4//
5// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
6// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
7// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
8// This file may not be copied, modified, or distributed except according to
9// those terms.
10
11//! Byte order-aware numeric primitives.
12//!
13//! This module contains equivalents of the native multi-byte integer types with
14//! no alignment requirement and supporting byte order conversions.
15//!
16//! For each native multi-byte integer type - `u16`, `i16`, `u32`, etc - and
17//! floating point type - `f32` and `f64` - an equivalent type is defined by
18//! this module - [`U16`], [`I16`], [`U32`], [`F32`], [`F64`], etc. Unlike their
19//! native counterparts, these types have alignment 1, and take a type parameter
20//! specifying the byte order in which the bytes are stored in memory. Each type
21//! implements this crate's relevant conversion and marker traits.
22//!
23//! These two properties, taken together, make these types useful for defining
24//! data structures whose memory layout matches a wire format such as that of a
25//! network protocol or a file format. Such formats often have multi-byte values
26//! at offsets that do not respect the alignment requirements of the equivalent
27//! native types, and stored in a byte order not necessarily the same as that of
28//! the target platform.
29//!
30//! Type aliases are provided for common byte orders in the [`big_endian`],
31//! [`little_endian`], [`network_endian`], and [`native_endian`] submodules.
32//! Note that network-endian is a synonym for big-endian.
33//!
34//! # Example
35//!
36//! One use of these types is for representing network packet formats, such as
37//! UDP:
38//!
39//! ```rust
40//! use zerocopy::{*, byteorder::network_endian::U16};
41//! # use zerocopy_derive::*;
42//!
43//! #[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
44//! #[repr(C)]
45//! struct UdpHeader {
46//!     src_port: U16,
47//!     dst_port: U16,
48//!     length: U16,
49//!     checksum: U16,
50//! }
51//!
52//! #[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
53//! #[repr(C, packed)]
54//! struct UdpPacket {
55//!     header: UdpHeader,
56//!     body: [u8],
57//! }
58//!
59//! impl UdpPacket {
60//!     fn parse(bytes: &[u8]) -> Option<&UdpPacket> {
61//!         UdpPacket::ref_from_bytes(bytes).ok()
62//!     }
63//! }
64//! ```
65
66use core::{
67    convert::{TryFrom, TryInto},
68    fmt::{Binary, Debug, LowerHex, Octal, UpperHex},
69    hash::Hash,
70    num::TryFromIntError,
71};
72
73use super::*;
74
75/// A type-level representation of byte order.
76///
77/// This type is implemented by [`BigEndian`] and [`LittleEndian`], which
78/// represent big-endian and little-endian byte order respectively. This module
79/// also provides a number of useful aliases for those types: [`NativeEndian`],
80/// [`NetworkEndian`], [`BE`], and [`LE`].
81///
82/// `ByteOrder` types can be used to specify the byte order of the types in this
83/// module - for example, [`U32<BigEndian>`] is a 32-bit integer stored in
84/// big-endian byte order.
85///
86/// [`U32<BigEndian>`]: U32
87pub trait ByteOrder:
88    Copy + Clone + Debug + Display + Eq + PartialEq + Ord + PartialOrd + Hash + private::Sealed
89{
90    #[doc(hidden)]
91    const ORDER: Order;
92}
93
94mod private {
95    pub trait Sealed {}
96
97    impl Sealed for super::BigEndian {}
98    impl Sealed for super::LittleEndian {}
99}
100
101#[allow(missing_copy_implementations, missing_debug_implementations)]
102#[doc(hidden)]
103pub enum Order {
104    BigEndian,
105    LittleEndian,
106}
107
108/// Big-endian byte order.
109///
110/// See [`ByteOrder`] for more details.
111#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
112pub enum BigEndian {}
113
114impl ByteOrder for BigEndian {
115    const ORDER: Order = Order::BigEndian;
116}
117
118impl Display for BigEndian {
119    #[inline]
120    fn fmt(&self, _: &mut Formatter<'_>) -> fmt::Result {
121        match *self {}
122    }
123}
124
125/// Little-endian byte order.
126///
127/// See [`ByteOrder`] for more details.
128#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
129pub enum LittleEndian {}
130
131impl ByteOrder for LittleEndian {
132    const ORDER: Order = Order::LittleEndian;
133}
134
135impl Display for LittleEndian {
136    #[inline]
137    fn fmt(&self, _: &mut Formatter<'_>) -> fmt::Result {
138        match *self {}
139    }
140}
141
142/// The endianness used by this platform.
143///
144/// This is a type alias for [`BigEndian`] or [`LittleEndian`] depending on the
145/// endianness of the target platform.
146#[cfg(target_endian = "big")]
147pub type NativeEndian = BigEndian;
148
149/// The endianness used by this platform.
150///
151/// This is a type alias for [`BigEndian`] or [`LittleEndian`] depending on the
152/// endianness of the target platform.
153#[cfg(target_endian = "little")]
154pub type NativeEndian = LittleEndian;
155
156/// The endianness used in many network protocols.
157///
158/// This is a type alias for [`BigEndian`].
159pub type NetworkEndian = BigEndian;
160
161/// A type alias for [`BigEndian`].
162pub type BE = BigEndian;
163
164/// A type alias for [`LittleEndian`].
165pub type LE = LittleEndian;
166
167macro_rules! impl_dbg_trait {
168    ($name:ident, $native:ident) => {
169        impl<O: ByteOrder> Debug for $name<O> {
170            #[inline]
171            fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
172                // This results in a format like "U16(42)".
173                f.debug_tuple(stringify!($name)).field(&self.get()).finish()
174            }
175        }
176    };
177}
178
179macro_rules! impl_dbg_traits {
180    ($name:ident, $native:ident, "floating point number") => {
181        #[cfg(not(no_fp_fmt_parse))]
182        impl_dbg_trait!($name, $native);
183
184        #[cfg(no_fp_fmt_parse)]
185        impl<O: ByteOrder> Debug for $name<O> {
186            #[inline]
187            fn fmt(&self, _f: &mut Formatter<'_>) -> fmt::Result {
188                panic!("floating point support is turned off");
189            }
190        }
191    };
192    ($name:ident, $native:ident, "unsigned integer") => {
193        impl_dbg_traits!($name, $native, @all_types);
194    };
195    ($name:ident, $native:ident, "signed integer") => {
196        impl_dbg_traits!($name, $native, @all_types);
197    };
198    ($name:ident, $native:ident, @all_types) => {
199        impl_dbg_trait!($name, $native);
200    };
201}
202
203macro_rules! impl_fmt_trait {
204    ($name:ident, $native:ident, $trait:ident) => {
205        impl<O: ByteOrder> $trait for $name<O> {
206            #[inline(always)]
207            fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
208                $trait::fmt(&self.get(), f)
209            }
210        }
211    };
212}
213
214macro_rules! impl_fmt_traits {
215    ($name:ident, $native:ident, "floating point number") => {
216        #[cfg(not(no_fp_fmt_parse))]
217        impl_fmt_trait!($name, $native, Display);
218    };
219    ($name:ident, $native:ident, "unsigned integer") => {
220        impl_fmt_traits!($name, $native, @all_types);
221    };
222    ($name:ident, $native:ident, "signed integer") => {
223        impl_fmt_traits!($name, $native, @all_types);
224    };
225    ($name:ident, $native:ident, @all_types) => {
226        impl_fmt_trait!($name, $native, Display);
227        impl_fmt_trait!($name, $native, Octal);
228        impl_fmt_trait!($name, $native, LowerHex);
229        impl_fmt_trait!($name, $native, UpperHex);
230        impl_fmt_trait!($name, $native, Binary);
231    };
232}
233
234macro_rules! impl_ops_traits {
235    ($name:ident, $native:ident, "floating point number") => {
236        impl_ops_traits!($name, $native, @all_types);
237        impl_ops_traits!($name, $native, @signed_integer_floating_point);
238
239        impl<O: ByteOrder> PartialOrd for $name<O> {
240            #[inline(always)]
241            fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
242                self.get().partial_cmp(&other.get())
243            }
244        }
245    };
246    ($name:ident, $native:ident, "unsigned integer") => {
247        impl_ops_traits!($name, $native, @signed_unsigned_integer);
248        impl_ops_traits!($name, $native, @all_types);
249    };
250    ($name:ident, $native:ident, "signed integer") => {
251        impl_ops_traits!($name, $native, @signed_unsigned_integer);
252        impl_ops_traits!($name, $native, @signed_integer_floating_point);
253        impl_ops_traits!($name, $native, @all_types);
254    };
255    ($name:ident, $native:ident, @signed_unsigned_integer) => {
256        impl_ops_traits!(@without_byteorder_swap $name, $native, BitAnd, bitand, BitAndAssign, bitand_assign);
257        impl_ops_traits!(@without_byteorder_swap $name, $native, BitOr, bitor, BitOrAssign, bitor_assign);
258        impl_ops_traits!(@without_byteorder_swap $name, $native, BitXor, bitxor, BitXorAssign, bitxor_assign);
259        impl_ops_traits!(@with_byteorder_swap $name, $native, Shl, shl, ShlAssign, shl_assign);
260        impl_ops_traits!(@with_byteorder_swap $name, $native, Shr, shr, ShrAssign, shr_assign);
261
262        impl<O> core::ops::Not for $name<O> {
263            type Output = $name<O>;
264
265            #[inline(always)]
266            fn not(self) -> $name<O> {
267                 let self_native = $native::from_ne_bytes(self.0);
268                 $name((!self_native).to_ne_bytes(), PhantomData)
269            }
270        }
271
272        impl<O: ByteOrder> PartialOrd for $name<O> {
273            #[inline(always)]
274            fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
275                Some(self.cmp(other))
276            }
277        }
278
279        impl<O: ByteOrder> Ord for $name<O> {
280            #[inline(always)]
281            fn cmp(&self, other: &Self) -> Ordering {
282                self.get().cmp(&other.get())
283            }
284        }
285
286        impl<O: ByteOrder> PartialOrd<$native> for $name<O> {
287            #[inline(always)]
288            fn partial_cmp(&self, other: &$native) -> Option<Ordering> {
289                self.get().partial_cmp(other)
290            }
291        }
292    };
293    ($name:ident, $native:ident, @signed_integer_floating_point) => {
294        impl<O: ByteOrder> core::ops::Neg for $name<O> {
295            type Output = $name<O>;
296
297            #[inline(always)]
298            fn neg(self) -> $name<O> {
299                let self_native: $native = self.get();
300                #[allow(clippy::arithmetic_side_effects)]
301                $name::<O>::new(-self_native)
302            }
303        }
304    };
305    ($name:ident, $native:ident, @all_types) => {
306        impl_ops_traits!(@with_byteorder_swap $name, $native, Add, add, AddAssign, add_assign);
307        impl_ops_traits!(@with_byteorder_swap $name, $native, Div, div, DivAssign, div_assign);
308        impl_ops_traits!(@with_byteorder_swap $name, $native, Mul, mul, MulAssign, mul_assign);
309        impl_ops_traits!(@with_byteorder_swap $name, $native, Rem, rem, RemAssign, rem_assign);
310        impl_ops_traits!(@with_byteorder_swap $name, $native, Sub, sub, SubAssign, sub_assign);
311    };
312    (@with_byteorder_swap $name:ident, $native:ident, $trait:ident, $method:ident, $trait_assign:ident, $method_assign:ident) => {
313        impl<O: ByteOrder> core::ops::$trait<$name<O>> for $name<O> {
314            type Output = $name<O>;
315
316            #[inline(always)]
317            fn $method(self, rhs: $name<O>) -> $name<O> {
318                let self_native: $native = self.get();
319                let rhs_native: $native = rhs.get();
320                let result_native = core::ops::$trait::$method(self_native, rhs_native);
321                $name::<O>::new(result_native)
322            }
323        }
324
325        impl<O: ByteOrder> core::ops::$trait<$name<O>> for $native {
326            type Output = $name<O>;
327
328            #[inline(always)]
329            fn $method(self, rhs: $name<O>) -> $name<O> {
330                let rhs_native: $native = rhs.get();
331                let result_native = core::ops::$trait::$method(self, rhs_native);
332                $name::<O>::new(result_native)
333            }
334        }
335
336        impl<O: ByteOrder> core::ops::$trait<$native> for $name<O> {
337            type Output = $name<O>;
338
339            #[inline(always)]
340            fn $method(self, rhs: $native) -> $name<O> {
341                let self_native: $native = self.get();
342                let result_native = core::ops::$trait::$method(self_native, rhs);
343                $name::<O>::new(result_native)
344            }
345        }
346
347        impl<O: ByteOrder> core::ops::$trait_assign<$name<O>> for $name<O> {
348            #[inline(always)]
349            fn $method_assign(&mut self, rhs: $name<O>) {
350                *self = core::ops::$trait::$method(*self, rhs);
351            }
352        }
353
354        impl<O: ByteOrder> core::ops::$trait_assign<$name<O>> for $native {
355            #[inline(always)]
356            fn $method_assign(&mut self, rhs: $name<O>) {
357                let rhs_native: $native = rhs.get();
358                *self = core::ops::$trait::$method(*self, rhs_native);
359            }
360        }
361
362        impl<O: ByteOrder> core::ops::$trait_assign<$native> for $name<O> {
363            #[inline(always)]
364            fn $method_assign(&mut self, rhs: $native) {
365                *self = core::ops::$trait::$method(*self, rhs);
366            }
367        }
368    };
369    // Implement traits in terms of the same trait on the native type, but
370    // without performing a byte order swap when both operands are byteorder
371    // types. This only works for bitwise operations like `&`, `|`, etc.
372    //
373    // When only one operand is a byteorder type, we still need to perform a
374    // byteorder swap.
375    (@without_byteorder_swap $name:ident, $native:ident, $trait:ident, $method:ident, $trait_assign:ident, $method_assign:ident) => {
376        impl<O: ByteOrder> core::ops::$trait<$name<O>> for $name<O> {
377            type Output = $name<O>;
378
379            #[inline(always)]
380            fn $method(self, rhs: $name<O>) -> $name<O> {
381                let self_native = $native::from_ne_bytes(self.0);
382                let rhs_native = $native::from_ne_bytes(rhs.0);
383                let result_native = core::ops::$trait::$method(self_native, rhs_native);
384                $name(result_native.to_ne_bytes(), PhantomData)
385            }
386        }
387
388        impl<O: ByteOrder> core::ops::$trait<$name<O>> for $native {
389            type Output = $name<O>;
390
391            #[inline(always)]
392            fn $method(self, rhs: $name<O>) -> $name<O> {
393                // No runtime cost - just byte packing
394                let rhs_native = $native::from_ne_bytes(rhs.0);
395                // (Maybe) runtime cost - byte order swap
396                let slf_byteorder = $name::<O>::new(self);
397                // No runtime cost - just byte packing
398                let slf_native = $native::from_ne_bytes(slf_byteorder.0);
399                // Runtime cost - perform the operation
400                let result_native = core::ops::$trait::$method(slf_native, rhs_native);
401                // No runtime cost - just byte unpacking
402                $name(result_native.to_ne_bytes(), PhantomData)
403            }
404        }
405
406        impl<O: ByteOrder> core::ops::$trait<$native> for $name<O> {
407            type Output = $name<O>;
408
409            #[inline(always)]
410            fn $method(self, rhs: $native) -> $name<O> {
411                // (Maybe) runtime cost - byte order swap
412                let rhs_byteorder = $name::<O>::new(rhs);
413                // No runtime cost - just byte packing
414                let rhs_native = $native::from_ne_bytes(rhs_byteorder.0);
415                // No runtime cost - just byte packing
416                let slf_native = $native::from_ne_bytes(self.0);
417                // Runtime cost - perform the operation
418                let result_native = core::ops::$trait::$method(slf_native, rhs_native);
419                // No runtime cost - just byte unpacking
420                $name(result_native.to_ne_bytes(), PhantomData)
421            }
422        }
423
424        impl<O: ByteOrder> core::ops::$trait_assign<$name<O>> for $name<O> {
425            #[inline(always)]
426            fn $method_assign(&mut self, rhs: $name<O>) {
427                *self = core::ops::$trait::$method(*self, rhs);
428            }
429        }
430
431        impl<O: ByteOrder> core::ops::$trait_assign<$name<O>> for $native {
432            #[inline(always)]
433            fn $method_assign(&mut self, rhs: $name<O>) {
434                // (Maybe) runtime cost - byte order swap
435                let rhs_native = rhs.get();
436                // Runtime cost - perform the operation
437                *self = core::ops::$trait::$method(*self, rhs_native);
438            }
439        }
440
441        impl<O: ByteOrder> core::ops::$trait_assign<$native> for $name<O> {
442            #[inline(always)]
443            fn $method_assign(&mut self, rhs: $native) {
444                *self = core::ops::$trait::$method(*self, rhs);
445            }
446        }
447    };
448}
449
450macro_rules! doc_comment {
451    ($x:expr, $($tt:tt)*) => {
452        #[doc = $x]
453        $($tt)*
454    };
455}
456
457macro_rules! define_max_value_constant {
458    ($name:ident, $bytes:expr, "unsigned integer") => {
459        /// The maximum value.
460        ///
461        /// This constant should be preferred to constructing a new value using
462        /// `new`, as `new` may perform an endianness swap depending on the
463        /// endianness `O` and the endianness of the platform.
464        pub const MAX_VALUE: $name<O> = $name([0xFFu8; $bytes], PhantomData);
465    };
466    // We don't provide maximum and minimum value constants for signed values
467    // and floats because there's no way to do it generically - it would require
468    // a different value depending on the value of the `ByteOrder` type
469    // parameter. Currently, one workaround would be to provide implementations
470    // for concrete implementations of that trait. In the long term, if we are
471    // ever able to make the `new` constructor a const fn, we could use that
472    // instead.
473    ($name:ident, $bytes:expr, "signed integer") => {};
474    ($name:ident, $bytes:expr, "floating point number") => {};
475}
476
477macro_rules! define_type {
478    (
479        $article:ident,
480        $description:expr,
481        $name:ident,
482        $native:ident,
483        $bits:expr,
484        $bytes:expr,
485        $from_be_fn:path,
486        $to_be_fn:path,
487        $from_le_fn:path,
488        $to_le_fn:path,
489        $number_kind:tt,
490        [$($larger_native:ty),*],
491        [$($larger_native_try:ty),*],
492        [$($larger_byteorder:ident),*],
493        [$($larger_byteorder_try:ident),*]
494    ) => {
495        doc_comment! {
496            concat!($description, " stored in a given byte order.
497
498`", stringify!($name), "` is like the native `", stringify!($native), "` type with
499two major differences: First, it has no alignment requirement (its alignment is 1).
500Second, the endianness of its memory layout is given by the type parameter `O`,
501which can be any type which implements [`ByteOrder`]. In particular, this refers
502to [`BigEndian`], [`LittleEndian`], [`NativeEndian`], and [`NetworkEndian`].
503
504", stringify!($article), " `", stringify!($name), "` can be constructed using
505the [`new`] method, and its contained value can be obtained as a native
506`",stringify!($native), "` using the [`get`] method, or updated in place with
507the [`set`] method. In all cases, if the endianness `O` is not the same as the
508endianness of the current platform, an endianness swap will be performed in
509order to uphold the invariants that a) the layout of `", stringify!($name), "`
510has endianness `O` and that, b) the layout of `", stringify!($native), "` has
511the platform's native endianness.
512
513`", stringify!($name), "` implements [`FromBytes`], [`IntoBytes`], and [`Unaligned`],
514making it useful for parsing and serialization. See the module documentation for an
515example of how it can be used for parsing UDP packets.
516
517[`new`]: crate::byteorder::", stringify!($name), "::new
518[`get`]: crate::byteorder::", stringify!($name), "::get
519[`set`]: crate::byteorder::", stringify!($name), "::set
520[`FromBytes`]: crate::FromBytes
521[`IntoBytes`]: crate::IntoBytes
522[`Unaligned`]: crate::Unaligned"),
523            #[derive(Copy, Clone, Eq, PartialEq, Hash)]
524            #[cfg_attr(any(feature = "derive", test), derive(KnownLayout, Immutable, FromBytes, IntoBytes, Unaligned))]
525            #[repr(transparent)]
526            pub struct $name<O>([u8; $bytes], PhantomData<O>);
527        }
528
529        #[cfg(not(any(feature = "derive", test)))]
530        impl_known_layout!(O => $name<O>);
531
532        #[allow(unused_unsafe)] // Unused when `feature = "derive"`.
533        // SAFETY: `$name<O>` is `repr(transparent)`, and so it has the same
534        // layout as its only non-zero field, which is a `u8` array. `u8` arrays
535        // are `Immutable`, `TryFromBytes`, `FromZeros`, `FromBytes`,
536        // `IntoBytes`, and `Unaligned`.
537        #[allow(clippy::multiple_unsafe_ops_per_block)]
538        const _: () = unsafe {
539            impl_or_verify!(O => Immutable for $name<O>);
540            impl_or_verify!(O => TryFromBytes for $name<O>);
541            impl_or_verify!(O => FromZeros for $name<O>);
542            impl_or_verify!(O => FromBytes for $name<O>);
543            impl_or_verify!(O => IntoBytes for $name<O>);
544            impl_or_verify!(O => Unaligned for $name<O>);
545        };
546
547        impl<O> Default for $name<O> {
548            #[inline(always)]
549            fn default() -> $name<O> {
550                $name::ZERO
551            }
552        }
553
554        impl<O> $name<O> {
555            /// The value zero.
556            ///
557            /// This constant should be preferred to constructing a new value
558            /// using `new`, as `new` may perform an endianness swap depending
559            /// on the endianness and platform.
560            pub const ZERO: $name<O> = $name([0u8; $bytes], PhantomData);
561
562            define_max_value_constant!($name, $bytes, $number_kind);
563
564            /// Constructs a new value from bytes which are already in `O` byte
565            /// order.
566            #[must_use = "has no side effects"]
567            #[inline(always)]
568            pub const fn from_bytes(bytes: [u8; $bytes]) -> $name<O> {
569                $name(bytes, PhantomData)
570            }
571
572            /// Extracts the bytes of `self` without swapping the byte order.
573            ///
574            /// The returned bytes will be in `O` byte order.
575            #[must_use = "has no side effects"]
576            #[inline(always)]
577            pub const fn to_bytes(self) -> [u8; $bytes] {
578                self.0
579            }
580        }
581
582        impl<O: ByteOrder> $name<O> {
583            maybe_const_trait_bounded_fn! {
584                /// Constructs a new value, possibly performing an endianness
585                /// swap to guarantee that the returned value has endianness
586                /// `O`.
587                #[must_use = "has no side effects"]
588                #[inline(always)]
589                pub const fn new(n: $native) -> $name<O> {
590                    let bytes = match O::ORDER {
591                        Order::BigEndian => $to_be_fn(n),
592                        Order::LittleEndian => $to_le_fn(n),
593                    };
594
595                    $name(bytes, PhantomData)
596                }
597            }
598
599            maybe_const_trait_bounded_fn! {
600                /// Returns the value as a primitive type, possibly performing
601                /// an endianness swap to guarantee that the return value has
602                /// the endianness of the native platform.
603                #[must_use = "has no side effects"]
604                #[inline(always)]
605                pub const fn get(self) -> $native {
606                    match O::ORDER {
607                        Order::BigEndian => $from_be_fn(self.0),
608                        Order::LittleEndian => $from_le_fn(self.0),
609                    }
610                }
611            }
612
613            /// Updates the value in place as a primitive type, possibly
614            /// performing an endianness swap to guarantee that the stored value
615            /// has the endianness `O`.
616            #[inline(always)]
617            pub fn set(&mut self, n: $native) {
618                *self = Self::new(n);
619            }
620        }
621
622        // The reasoning behind which traits to implement here is to only
623        // implement traits which won't cause inference issues. Notably,
624        // comparison traits like PartialEq and PartialOrd tend to cause
625        // inference issues.
626
627        impl<O: ByteOrder> From<$name<O>> for [u8; $bytes] {
628            #[inline(always)]
629            fn from(x: $name<O>) -> [u8; $bytes] {
630                x.0
631            }
632        }
633
634        impl<O: ByteOrder> From<[u8; $bytes]> for $name<O> {
635            #[inline(always)]
636            fn from(bytes: [u8; $bytes]) -> $name<O> {
637                $name(bytes, PhantomData)
638            }
639        }
640
641        impl<O: ByteOrder> From<$name<O>> for $native {
642            #[inline(always)]
643            fn from(x: $name<O>) -> $native {
644                x.get()
645            }
646        }
647
648        impl<O: ByteOrder> From<$native> for $name<O> {
649            #[inline(always)]
650            fn from(x: $native) -> $name<O> {
651                $name::new(x)
652            }
653        }
654
655        $(
656            impl<O: ByteOrder> From<$name<O>> for $larger_native {
657                #[inline(always)]
658                fn from(x: $name<O>) -> $larger_native {
659                    x.get().into()
660                }
661            }
662        )*
663
664        $(
665            impl<O: ByteOrder> TryFrom<$larger_native_try> for $name<O> {
666                type Error = TryFromIntError;
667                #[inline(always)]
668                fn try_from(x: $larger_native_try) -> Result<$name<O>, TryFromIntError> {
669                    $native::try_from(x).map($name::new)
670                }
671            }
672        )*
673
674        $(
675            impl<O: ByteOrder, P: ByteOrder> From<$name<O>> for $larger_byteorder<P> {
676                #[inline(always)]
677                fn from(x: $name<O>) -> $larger_byteorder<P> {
678                    $larger_byteorder::new(x.get().into())
679                }
680            }
681        )*
682
683        $(
684            impl<O: ByteOrder, P: ByteOrder> TryFrom<$larger_byteorder_try<P>> for $name<O> {
685                type Error = TryFromIntError;
686                #[inline(always)]
687                fn try_from(x: $larger_byteorder_try<P>) -> Result<$name<O>, TryFromIntError> {
688                    x.get().try_into().map($name::new)
689                }
690            }
691        )*
692
693        impl<O> AsRef<[u8; $bytes]> for $name<O> {
694            #[inline(always)]
695            fn as_ref(&self) -> &[u8; $bytes] {
696                &self.0
697            }
698        }
699
700        impl<O> AsMut<[u8; $bytes]> for $name<O> {
701            #[inline(always)]
702            fn as_mut(&mut self) -> &mut [u8; $bytes] {
703                &mut self.0
704            }
705        }
706
707        impl<O> PartialEq<$name<O>> for [u8; $bytes] {
708            #[inline(always)]
709            fn eq(&self, other: &$name<O>) -> bool {
710                self.eq(&other.0)
711            }
712        }
713
714        impl<O> PartialEq<[u8; $bytes]> for $name<O> {
715            #[inline(always)]
716            fn eq(&self, other: &[u8; $bytes]) -> bool {
717                self.0.eq(other)
718            }
719        }
720
721        impl<O: ByteOrder> PartialEq<$native> for $name<O> {
722            #[inline(always)]
723            fn eq(&self, other: &$native) -> bool {
724                self.get().eq(other)
725            }
726        }
727
728        impl_dbg_traits!($name, $native, $number_kind);
729        impl_fmt_traits!($name, $native, $number_kind);
730        impl_ops_traits!($name, $native, $number_kind);
731    };
732}
733
734define_type!(
735    A,
736    "A 16-bit unsigned integer",
737    U16,
738    u16,
739    16,
740    2,
741    u16::from_be_bytes,
742    u16::to_be_bytes,
743    u16::from_le_bytes,
744    u16::to_le_bytes,
745    "unsigned integer",
746    [u32, u64, u128, usize],
747    [u32, u64, u128, usize],
748    [U32, U64, U128, Usize],
749    [U32, U64, U128, Usize]
750);
751define_type!(
752    A,
753    "A 32-bit unsigned integer",
754    U32,
755    u32,
756    32,
757    4,
758    u32::from_be_bytes,
759    u32::to_be_bytes,
760    u32::from_le_bytes,
761    u32::to_le_bytes,
762    "unsigned integer",
763    [u64, u128],
764    [u64, u128],
765    [U64, U128],
766    [U64, U128]
767);
768define_type!(
769    A,
770    "A 64-bit unsigned integer",
771    U64,
772    u64,
773    64,
774    8,
775    u64::from_be_bytes,
776    u64::to_be_bytes,
777    u64::from_le_bytes,
778    u64::to_le_bytes,
779    "unsigned integer",
780    [u128],
781    [u128],
782    [U128],
783    [U128]
784);
785define_type!(
786    A,
787    "A 128-bit unsigned integer",
788    U128,
789    u128,
790    128,
791    16,
792    u128::from_be_bytes,
793    u128::to_be_bytes,
794    u128::from_le_bytes,
795    u128::to_le_bytes,
796    "unsigned integer",
797    [],
798    [],
799    [],
800    []
801);
802define_type!(
803    A,
804    "A word-sized unsigned integer",
805    Usize,
806    usize,
807    mem::size_of::<usize>() * 8,
808    mem::size_of::<usize>(),
809    usize::from_be_bytes,
810    usize::to_be_bytes,
811    usize::from_le_bytes,
812    usize::to_le_bytes,
813    "unsigned integer",
814    [],
815    [],
816    [],
817    []
818);
819define_type!(
820    An,
821    "A 16-bit signed integer",
822    I16,
823    i16,
824    16,
825    2,
826    i16::from_be_bytes,
827    i16::to_be_bytes,
828    i16::from_le_bytes,
829    i16::to_le_bytes,
830    "signed integer",
831    [i32, i64, i128, isize],
832    [i32, i64, i128, isize],
833    [I32, I64, I128, Isize],
834    [I32, I64, I128, Isize]
835);
836define_type!(
837    An,
838    "A 32-bit signed integer",
839    I32,
840    i32,
841    32,
842    4,
843    i32::from_be_bytes,
844    i32::to_be_bytes,
845    i32::from_le_bytes,
846    i32::to_le_bytes,
847    "signed integer",
848    [i64, i128],
849    [i64, i128],
850    [I64, I128],
851    [I64, I128]
852);
853define_type!(
854    An,
855    "A 64-bit signed integer",
856    I64,
857    i64,
858    64,
859    8,
860    i64::from_be_bytes,
861    i64::to_be_bytes,
862    i64::from_le_bytes,
863    i64::to_le_bytes,
864    "signed integer",
865    [i128],
866    [i128],
867    [I128],
868    [I128]
869);
870define_type!(
871    An,
872    "A 128-bit signed integer",
873    I128,
874    i128,
875    128,
876    16,
877    i128::from_be_bytes,
878    i128::to_be_bytes,
879    i128::from_le_bytes,
880    i128::to_le_bytes,
881    "signed integer",
882    [],
883    [],
884    [],
885    []
886);
887define_type!(
888    An,
889    "A word-sized signed integer",
890    Isize,
891    isize,
892    mem::size_of::<isize>() * 8,
893    mem::size_of::<isize>(),
894    isize::from_be_bytes,
895    isize::to_be_bytes,
896    isize::from_le_bytes,
897    isize::to_le_bytes,
898    "signed integer",
899    [],
900    [],
901    [],
902    []
903);
904
905// FIXME(https://github.com/rust-lang/rust/issues/72447): Use the endianness
906// conversion methods directly once those are const-stable.
907macro_rules! define_float_conversion {
908    ($ty:ty, $bits:ident, $bytes:expr, $mod:ident) => {
909        mod $mod {
910            use super::*;
911
912            define_float_conversion!($ty, $bits, $bytes, from_be_bytes, to_be_bytes);
913            define_float_conversion!($ty, $bits, $bytes, from_le_bytes, to_le_bytes);
914        }
915    };
916    ($ty:ty, $bits:ident, $bytes:expr, $from:ident, $to:ident) => {
917        // Clippy: The suggestion of using `from_bits()` instead doesn't work
918        // because `from_bits` is not const-stable on our MSRV.
919        #[allow(clippy::unnecessary_transmutes)]
920        pub(crate) const fn $from(bytes: [u8; $bytes]) -> $ty {
921            transmute!($bits::$from(bytes))
922        }
923
924        pub(crate) const fn $to(f: $ty) -> [u8; $bytes] {
925            // Clippy: The suggestion of using `f.to_bits()` instead doesn't
926            // work because `to_bits` is not const-stable on our MSRV.
927            #[allow(clippy::unnecessary_transmutes)]
928            let bits: $bits = transmute!(f);
929            bits.$to()
930        }
931    };
932}
933
934define_float_conversion!(f32, u32, 4, f32_ext);
935define_float_conversion!(f64, u64, 8, f64_ext);
936
937define_type!(
938    An,
939    "A 32-bit floating point number",
940    F32,
941    f32,
942    32,
943    4,
944    f32_ext::from_be_bytes,
945    f32_ext::to_be_bytes,
946    f32_ext::from_le_bytes,
947    f32_ext::to_le_bytes,
948    "floating point number",
949    [f64],
950    [],
951    [F64],
952    []
953);
954define_type!(
955    An,
956    "A 64-bit floating point number",
957    F64,
958    f64,
959    64,
960    8,
961    f64_ext::from_be_bytes,
962    f64_ext::to_be_bytes,
963    f64_ext::from_le_bytes,
964    f64_ext::to_le_bytes,
965    "floating point number",
966    [],
967    [],
968    [],
969    []
970);
971
972macro_rules! module {
973    ($name:ident, $trait:ident, $endianness_str:expr) => {
974        /// Numeric primitives stored in
975        #[doc = $endianness_str]
976        /// byte order.
977        pub mod $name {
978            use super::$trait;
979
980            module!(@ty U16,  $trait, "16-bit unsigned integer", $endianness_str);
981            module!(@ty U32,  $trait, "32-bit unsigned integer", $endianness_str);
982            module!(@ty U64,  $trait, "64-bit unsigned integer", $endianness_str);
983            module!(@ty U128, $trait, "128-bit unsigned integer", $endianness_str);
984            module!(@ty I16,  $trait, "16-bit signed integer", $endianness_str);
985            module!(@ty I32,  $trait, "32-bit signed integer", $endianness_str);
986            module!(@ty I64,  $trait, "64-bit signed integer", $endianness_str);
987            module!(@ty I128, $trait, "128-bit signed integer", $endianness_str);
988            module!(@ty F32,  $trait, "32-bit floating point number", $endianness_str);
989            module!(@ty F64,  $trait, "64-bit floating point number", $endianness_str);
990        }
991    };
992    (@ty $ty:ident, $trait:ident, $desc_str:expr, $endianness_str:expr) => {
993        /// A
994        #[doc = $desc_str]
995        /// stored in
996        #[doc = $endianness_str]
997        /// byte order.
998        pub type $ty = crate::byteorder::$ty<$trait>;
999    };
1000}
1001
1002module!(big_endian, BigEndian, "big-endian");
1003module!(little_endian, LittleEndian, "little-endian");
1004module!(network_endian, NetworkEndian, "network-endian");
1005module!(native_endian, NativeEndian, "native-endian");
1006
1007#[cfg(any(test, kani))]
1008mod tests {
1009    use super::*;
1010
1011    #[cfg(not(kani))]
1012    mod compatibility {
1013        pub(super) use rand::{
1014            distributions::{Distribution, Standard},
1015            rngs::SmallRng,
1016            Rng, SeedableRng,
1017        };
1018
1019        pub(crate) trait Arbitrary {}
1020
1021        impl<T> Arbitrary for T {}
1022    }
1023
1024    #[cfg(kani)]
1025    mod compatibility {
1026        pub(crate) use kani::Arbitrary;
1027
1028        pub(crate) struct SmallRng;
1029
1030        impl SmallRng {
1031            pub(crate) fn seed_from_u64(_state: u64) -> Self {
1032                Self
1033            }
1034        }
1035
1036        pub(crate) trait Rng {
1037            fn sample<T, D: Distribution<T>>(&mut self, _distr: D) -> T
1038            where
1039                T: Arbitrary,
1040            {
1041                kani::any()
1042            }
1043        }
1044
1045        impl Rng for SmallRng {}
1046
1047        pub(crate) trait Distribution<T> {}
1048        impl<T, U> Distribution<T> for U {}
1049
1050        pub(crate) struct Standard;
1051    }
1052
1053    use compatibility::*;
1054
1055    // A native integer type (u16, i32, etc).
1056    trait Native: Arbitrary + FromBytes + IntoBytes + Immutable + Copy + PartialEq + Debug {
1057        const ZERO: Self;
1058        const MAX_VALUE: Self;
1059
1060        type Distribution: Distribution<Self>;
1061        const DIST: Self::Distribution;
1062
1063        fn rand<R: Rng>(rng: &mut R) -> Self {
1064            rng.sample(Self::DIST)
1065        }
1066
1067        #[cfg_attr(kani, allow(unused))]
1068        fn checked_add(self, rhs: Self) -> Option<Self>;
1069
1070        #[cfg_attr(kani, allow(unused))]
1071        fn checked_div(self, rhs: Self) -> Option<Self>;
1072
1073        #[cfg_attr(kani, allow(unused))]
1074        fn checked_mul(self, rhs: Self) -> Option<Self>;
1075
1076        #[cfg_attr(kani, allow(unused))]
1077        fn checked_rem(self, rhs: Self) -> Option<Self>;
1078
1079        #[cfg_attr(kani, allow(unused))]
1080        fn checked_sub(self, rhs: Self) -> Option<Self>;
1081
1082        #[cfg_attr(kani, allow(unused))]
1083        fn checked_shl(self, rhs: Self) -> Option<Self>;
1084
1085        #[cfg_attr(kani, allow(unused))]
1086        fn checked_shr(self, rhs: Self) -> Option<Self>;
1087
1088        fn is_nan(self) -> bool;
1089
1090        /// For `f32` and `f64`, NaN values are not considered equal to
1091        /// themselves. This method is like `assert_eq!`, but it treats NaN
1092        /// values as equal.
1093        fn assert_eq_or_nan(self, other: Self) {
1094            let slf = (!self.is_nan()).then(|| self);
1095            let other = (!other.is_nan()).then(|| other);
1096            assert_eq!(slf, other);
1097        }
1098    }
1099
1100    trait ByteArray:
1101        FromBytes + IntoBytes + Immutable + Copy + AsRef<[u8]> + AsMut<[u8]> + Debug + Default + Eq
1102    {
1103        /// Invert the order of the bytes in the array.
1104        fn invert(self) -> Self;
1105    }
1106
1107    trait ByteOrderType:
1108        FromBytes + IntoBytes + Unaligned + Copy + Eq + Debug + Hash + From<Self::Native>
1109    {
1110        type Native: Native;
1111        type ByteArray: ByteArray;
1112
1113        const ZERO: Self;
1114
1115        fn new(native: Self::Native) -> Self;
1116        fn get(self) -> Self::Native;
1117        fn set(&mut self, native: Self::Native);
1118        fn from_bytes(bytes: Self::ByteArray) -> Self;
1119        fn into_bytes(self) -> Self::ByteArray;
1120
1121        /// For `f32` and `f64`, NaN values are not considered equal to
1122        /// themselves. This method is like `assert_eq!`, but it treats NaN
1123        /// values as equal.
1124        fn assert_eq_or_nan(self, other: Self) {
1125            let slf = (!self.get().is_nan()).then(|| self);
1126            let other = (!other.get().is_nan()).then(|| other);
1127            assert_eq!(slf, other);
1128        }
1129    }
1130
1131    trait ByteOrderTypeUnsigned: ByteOrderType {
1132        const MAX_VALUE: Self;
1133    }
1134
1135    macro_rules! impl_byte_array {
1136        ($bytes:expr) => {
1137            impl ByteArray for [u8; $bytes] {
1138                fn invert(mut self) -> [u8; $bytes] {
1139                    self.reverse();
1140                    self
1141                }
1142            }
1143        };
1144    }
1145
1146    impl_byte_array!(2);
1147    impl_byte_array!(4);
1148    impl_byte_array!(8);
1149    impl_byte_array!(16);
1150
1151    macro_rules! impl_byte_order_type_unsigned {
1152        ($name:ident, unsigned) => {
1153            impl<O: ByteOrder> ByteOrderTypeUnsigned for $name<O> {
1154                const MAX_VALUE: $name<O> = $name::MAX_VALUE;
1155            }
1156        };
1157        ($name:ident, signed) => {};
1158    }
1159
1160    macro_rules! impl_traits {
1161        ($name:ident, $native:ident, $sign:ident $(, @$float:ident)?) => {
1162            impl Native for $native {
1163                // For some types, `0 as $native` is required (for example, when
1164                // `$native` is a floating-point type; `0` is an integer), but
1165                // for other types, it's a trivial cast. In all cases, Clippy
1166                // thinks it's dangerous.
1167                #[allow(trivial_numeric_casts, clippy::as_conversions)]
1168                const ZERO: $native = 0 as $native;
1169                const MAX_VALUE: $native = $native::MAX;
1170
1171                type Distribution = Standard;
1172                const DIST: Standard = Standard;
1173
1174                impl_traits!(@float_dependent_methods $(@$float)?);
1175            }
1176
1177            impl<O: ByteOrder> ByteOrderType for $name<O> {
1178                type Native = $native;
1179                type ByteArray = [u8; mem::size_of::<$native>()];
1180
1181                const ZERO: $name<O> = $name::ZERO;
1182
1183                fn new(native: $native) -> $name<O> {
1184                    $name::new(native)
1185                }
1186
1187                fn get(self) -> $native {
1188                    $name::get(self)
1189                }
1190
1191                fn set(&mut self, native: $native) {
1192                    $name::set(self, native)
1193                }
1194
1195                fn from_bytes(bytes: [u8; mem::size_of::<$native>()]) -> $name<O> {
1196                    $name::from(bytes)
1197                }
1198
1199                fn into_bytes(self) -> [u8; mem::size_of::<$native>()] {
1200                    <[u8; mem::size_of::<$native>()]>::from(self)
1201                }
1202            }
1203
1204            impl_byte_order_type_unsigned!($name, $sign);
1205        };
1206        (@float_dependent_methods) => {
1207            fn checked_add(self, rhs: Self) -> Option<Self> { self.checked_add(rhs) }
1208            fn checked_div(self, rhs: Self) -> Option<Self> { self.checked_div(rhs) }
1209            fn checked_mul(self, rhs: Self) -> Option<Self> { self.checked_mul(rhs) }
1210            fn checked_rem(self, rhs: Self) -> Option<Self> { self.checked_rem(rhs) }
1211            fn checked_sub(self, rhs: Self) -> Option<Self> { self.checked_sub(rhs) }
1212            fn checked_shl(self, rhs: Self) -> Option<Self> { self.checked_shl(rhs.try_into().unwrap_or(u32::MAX)) }
1213            fn checked_shr(self, rhs: Self) -> Option<Self> { self.checked_shr(rhs.try_into().unwrap_or(u32::MAX)) }
1214            fn is_nan(self) -> bool { false }
1215        };
1216        (@float_dependent_methods @float) => {
1217            fn checked_add(self, rhs: Self) -> Option<Self> { Some(self + rhs) }
1218            fn checked_div(self, rhs: Self) -> Option<Self> { Some(self / rhs) }
1219            fn checked_mul(self, rhs: Self) -> Option<Self> { Some(self * rhs) }
1220            fn checked_rem(self, rhs: Self) -> Option<Self> { Some(self % rhs) }
1221            fn checked_sub(self, rhs: Self) -> Option<Self> { Some(self - rhs) }
1222            fn checked_shl(self, _rhs: Self) -> Option<Self> { unimplemented!() }
1223            fn checked_shr(self, _rhs: Self) -> Option<Self> { unimplemented!() }
1224            fn is_nan(self) -> bool { self.is_nan() }
1225        };
1226    }
1227
1228    impl_traits!(U16, u16, unsigned);
1229    impl_traits!(U32, u32, unsigned);
1230    impl_traits!(U64, u64, unsigned);
1231    impl_traits!(U128, u128, unsigned);
1232    impl_traits!(Usize, usize, unsigned);
1233    impl_traits!(I16, i16, signed);
1234    impl_traits!(I32, i32, signed);
1235    impl_traits!(I64, i64, signed);
1236    impl_traits!(I128, i128, signed);
1237    impl_traits!(Isize, isize, unsigned);
1238    impl_traits!(F32, f32, signed, @float);
1239    impl_traits!(F64, f64, signed, @float);
1240
1241    macro_rules! call_for_unsigned_types {
1242        ($fn:ident, $byteorder:ident) => {
1243            $fn::<U16<$byteorder>>();
1244            $fn::<U32<$byteorder>>();
1245            $fn::<U64<$byteorder>>();
1246            $fn::<U128<$byteorder>>();
1247            $fn::<Usize<$byteorder>>();
1248        };
1249    }
1250
1251    macro_rules! call_for_signed_types {
1252        ($fn:ident, $byteorder:ident) => {
1253            $fn::<I16<$byteorder>>();
1254            $fn::<I32<$byteorder>>();
1255            $fn::<I64<$byteorder>>();
1256            $fn::<I128<$byteorder>>();
1257            $fn::<Isize<$byteorder>>();
1258        };
1259    }
1260
1261    macro_rules! call_for_float_types {
1262        ($fn:ident, $byteorder:ident) => {
1263            $fn::<F32<$byteorder>>();
1264            $fn::<F64<$byteorder>>();
1265        };
1266    }
1267
1268    macro_rules! call_for_all_types {
1269        ($fn:ident, $byteorder:ident) => {
1270            call_for_unsigned_types!($fn, $byteorder);
1271            call_for_signed_types!($fn, $byteorder);
1272            call_for_float_types!($fn, $byteorder);
1273        };
1274    }
1275
1276    #[cfg(target_endian = "big")]
1277    type NonNativeEndian = LittleEndian;
1278    #[cfg(target_endian = "little")]
1279    type NonNativeEndian = BigEndian;
1280
1281    // We use a `u64` seed so that we can use `SeedableRng::seed_from_u64`.
1282    // `SmallRng`'s `SeedableRng::Seed` differs by platform, so if we wanted to
1283    // call `SeedableRng::from_seed`, which takes a `Seed`, we would need
1284    // conditional compilation by `target_pointer_width`.
1285    const RNG_SEED: u64 = 0x7A03CAE2F32B5B8F;
1286
1287    const RAND_ITERS: usize = if cfg!(any(miri, kani)) {
1288        // The tests below which use this constant used to take a very long time
1289        // on Miri, which slows down local development and CI jobs. We're not
1290        // using Miri to check for the correctness of our code, but rather its
1291        // soundness, and at least in the context of these particular tests, a
1292        // single loop iteration is just as good for surfacing UB as multiple
1293        // iterations are.
1294        //
1295        // As of the writing of this comment, here's one set of measurements:
1296        //
1297        //   $ # RAND_ITERS == 1
1298        //   $ cargo miri test -- -Z unstable-options --report-time endian
1299        //   test byteorder::tests::test_native_endian ... ok <0.049s>
1300        //   test byteorder::tests::test_non_native_endian ... ok <0.061s>
1301        //
1302        //   $ # RAND_ITERS == 1024
1303        //   $ cargo miri test -- -Z unstable-options --report-time endian
1304        //   test byteorder::tests::test_native_endian ... ok <25.716s>
1305        //   test byteorder::tests::test_non_native_endian ... ok <38.127s>
1306        1
1307    } else {
1308        1024
1309    };
1310
1311    #[test]
1312    fn test_const_methods() {
1313        use big_endian::*;
1314
1315        #[rustversion::since(1.61.0)]
1316        const _U: U16 = U16::new(0);
1317        #[rustversion::since(1.61.0)]
1318        const _NATIVE: u16 = _U.get();
1319        const _FROM_BYTES: U16 = U16::from_bytes([0, 1]);
1320        const _BYTES: [u8; 2] = _FROM_BYTES.to_bytes();
1321    }
1322
1323    #[cfg_attr(test, test)]
1324    #[cfg_attr(kani, kani::proof)]
1325    fn test_zero() {
1326        fn test_zero<T: ByteOrderType>() {
1327            assert_eq!(T::ZERO.get(), T::Native::ZERO);
1328        }
1329
1330        call_for_all_types!(test_zero, NativeEndian);
1331        call_for_all_types!(test_zero, NonNativeEndian);
1332    }
1333
1334    #[cfg_attr(test, test)]
1335    #[cfg_attr(kani, kani::proof)]
1336    fn test_max_value() {
1337        fn test_max_value<T: ByteOrderTypeUnsigned>() {
1338            assert_eq!(T::MAX_VALUE.get(), T::Native::MAX_VALUE);
1339        }
1340
1341        call_for_unsigned_types!(test_max_value, NativeEndian);
1342        call_for_unsigned_types!(test_max_value, NonNativeEndian);
1343    }
1344
1345    #[cfg_attr(test, test)]
1346    #[cfg_attr(kani, kani::proof)]
1347    fn test_endian() {
1348        fn test<T: ByteOrderType>(invert: bool) {
1349            let mut r = SmallRng::seed_from_u64(RNG_SEED);
1350            for _ in 0..RAND_ITERS {
1351                let native = T::Native::rand(&mut r);
1352                let mut bytes = T::ByteArray::default();
1353                bytes.as_mut_bytes().copy_from_slice(native.as_bytes());
1354                if invert {
1355                    bytes = bytes.invert();
1356                }
1357                let mut from_native = T::new(native);
1358                let from_bytes = T::from_bytes(bytes);
1359
1360                from_native.assert_eq_or_nan(from_bytes);
1361                from_native.get().assert_eq_or_nan(native);
1362                from_bytes.get().assert_eq_or_nan(native);
1363
1364                assert_eq!(from_native.into_bytes(), bytes);
1365                assert_eq!(from_bytes.into_bytes(), bytes);
1366
1367                let updated = T::Native::rand(&mut r);
1368                from_native.set(updated);
1369                from_native.get().assert_eq_or_nan(updated);
1370            }
1371        }
1372
1373        fn test_native<T: ByteOrderType>() {
1374            test::<T>(false);
1375        }
1376
1377        fn test_non_native<T: ByteOrderType>() {
1378            test::<T>(true);
1379        }
1380
1381        call_for_all_types!(test_native, NativeEndian);
1382        call_for_all_types!(test_non_native, NonNativeEndian);
1383    }
1384
1385    #[test]
1386    fn test_ops_impls() {
1387        // Test implementations of traits in `core::ops`. Some of these are
1388        // fairly banal, but some are optimized to perform the operation without
1389        // swapping byte order (namely, bit-wise operations which are identical
1390        // regardless of byte order). These are important to test, and while
1391        // we're testing those anyway, it's trivial to test all of the impls.
1392
1393        fn test<T, FTT, FTN, FNT, FNN, FNNChecked, FATT, FATN, FANT>(
1394            op_t_t: FTT,
1395            op_t_n: FTN,
1396            op_n_t: FNT,
1397            op_n_n: FNN,
1398            op_n_n_checked: Option<FNNChecked>,
1399            op_assign: Option<(FATT, FATN, FANT)>,
1400        ) where
1401            T: ByteOrderType,
1402            FTT: Fn(T, T) -> T,
1403            FTN: Fn(T, T::Native) -> T,
1404            FNT: Fn(T::Native, T) -> T,
1405            FNN: Fn(T::Native, T::Native) -> T::Native,
1406            FNNChecked: Fn(T::Native, T::Native) -> Option<T::Native>,
1407            FATT: Fn(&mut T, T),
1408            FATN: Fn(&mut T, T::Native),
1409            FANT: Fn(&mut T::Native, T),
1410        {
1411            let mut r = SmallRng::seed_from_u64(RNG_SEED);
1412            for _ in 0..RAND_ITERS {
1413                let n0 = T::Native::rand(&mut r);
1414                let n1 = T::Native::rand(&mut r);
1415                let t0 = T::new(n0);
1416                let t1 = T::new(n1);
1417
1418                // If this operation would overflow/underflow, skip it rather
1419                // than attempt to catch and recover from panics.
1420                if matches!(&op_n_n_checked, Some(checked) if checked(n0, n1).is_none()) {
1421                    continue;
1422                }
1423
1424                let t_t_res = op_t_t(t0, t1);
1425                let t_n_res = op_t_n(t0, n1);
1426                let n_t_res = op_n_t(n0, t1);
1427                let n_n_res = op_n_n(n0, n1);
1428
1429                // For `f32` and `f64`, NaN values are not considered equal to
1430                // themselves. We store `Option<f32>`/`Option<f64>` and store
1431                // NaN as `None` so they can still be compared.
1432                let val_or_none = |t: T| (!T::Native::is_nan(t.get())).then(|| t.get());
1433                let t_t_res = val_or_none(t_t_res);
1434                let t_n_res = val_or_none(t_n_res);
1435                let n_t_res = val_or_none(n_t_res);
1436                let n_n_res = (!T::Native::is_nan(n_n_res)).then(|| n_n_res);
1437                assert_eq!(t_t_res, n_n_res);
1438                assert_eq!(t_n_res, n_n_res);
1439                assert_eq!(n_t_res, n_n_res);
1440
1441                if let Some((op_assign_t_t, op_assign_t_n, op_assign_n_t)) = &op_assign {
1442                    let mut t_t_res = t0;
1443                    op_assign_t_t(&mut t_t_res, t1);
1444                    let mut t_n_res = t0;
1445                    op_assign_t_n(&mut t_n_res, n1);
1446                    let mut n_t_res = n0;
1447                    op_assign_n_t(&mut n_t_res, t1);
1448
1449                    // For `f32` and `f64`, NaN values are not considered equal to
1450                    // themselves. We store `Option<f32>`/`Option<f64>` and store
1451                    // NaN as `None` so they can still be compared.
1452                    let t_t_res = val_or_none(t_t_res);
1453                    let t_n_res = val_or_none(t_n_res);
1454                    let n_t_res = (!T::Native::is_nan(n_t_res)).then(|| n_t_res);
1455                    assert_eq!(t_t_res, n_n_res);
1456                    assert_eq!(t_n_res, n_n_res);
1457                    assert_eq!(n_t_res, n_n_res);
1458                }
1459            }
1460        }
1461
1462        macro_rules! test {
1463            (
1464                @binary
1465                $trait:ident,
1466                $method:ident $([$checked_method:ident])?,
1467                $trait_assign:ident,
1468                $method_assign:ident,
1469                $($call_for_macros:ident),*
1470            ) => {{
1471                fn t<T>()
1472                where
1473                    T: ByteOrderType,
1474                    T: core::ops::$trait<T, Output = T>,
1475                    T: core::ops::$trait<T::Native, Output = T>,
1476                    T::Native: core::ops::$trait<T, Output = T>,
1477                    T::Native: core::ops::$trait<T::Native, Output = T::Native>,
1478
1479                    T: core::ops::$trait_assign<T>,
1480                    T: core::ops::$trait_assign<T::Native>,
1481                    T::Native: core::ops::$trait_assign<T>,
1482                    T::Native: core::ops::$trait_assign<T::Native>,
1483                {
1484                    test::<T, _, _, _, _, _, _, _, _>(
1485                        core::ops::$trait::$method,
1486                        core::ops::$trait::$method,
1487                        core::ops::$trait::$method,
1488                        core::ops::$trait::$method,
1489                        {
1490                            #[allow(unused_mut, unused_assignments)]
1491                            let mut op_native_checked = None::<fn(T::Native, T::Native) -> Option<T::Native>>;
1492                            $(
1493                                op_native_checked = Some(T::Native::$checked_method);
1494                            )?
1495                            op_native_checked
1496                        },
1497                        Some((
1498                            <T as core::ops::$trait_assign<T>>::$method_assign,
1499                            <T as core::ops::$trait_assign::<T::Native>>::$method_assign,
1500                            <T::Native as core::ops::$trait_assign::<T>>::$method_assign
1501                        )),
1502                    );
1503                }
1504
1505                $(
1506                    $call_for_macros!(t, NativeEndian);
1507                    $call_for_macros!(t, NonNativeEndian);
1508                )*
1509            }};
1510            (
1511                @unary
1512                $trait:ident,
1513                $method:ident,
1514                $($call_for_macros:ident),*
1515            ) => {{
1516                fn t<T>()
1517                where
1518                    T: ByteOrderType,
1519                    T: core::ops::$trait<Output = T>,
1520                    T::Native: core::ops::$trait<Output = T::Native>,
1521                {
1522                    test::<T, _, _, _, _, _, _, _, _>(
1523                        |slf, _rhs| core::ops::$trait::$method(slf),
1524                        |slf, _rhs| core::ops::$trait::$method(slf),
1525                        |slf, _rhs| core::ops::$trait::$method(slf).into(),
1526                        |slf, _rhs| core::ops::$trait::$method(slf),
1527                        None::<fn(T::Native, T::Native) -> Option<T::Native>>,
1528                        None::<(fn(&mut T, T), fn(&mut T, T::Native), fn(&mut T::Native, T))>,
1529                    );
1530                }
1531
1532                $(
1533                    $call_for_macros!(t, NativeEndian);
1534                    $call_for_macros!(t, NonNativeEndian);
1535                )*
1536            }};
1537        }
1538
1539        test!(@binary Add, add[checked_add], AddAssign, add_assign, call_for_all_types);
1540        test!(@binary Div, div[checked_div], DivAssign, div_assign, call_for_all_types);
1541        test!(@binary Mul, mul[checked_mul], MulAssign, mul_assign, call_for_all_types);
1542        test!(@binary Rem, rem[checked_rem], RemAssign, rem_assign, call_for_all_types);
1543        test!(@binary Sub, sub[checked_sub], SubAssign, sub_assign, call_for_all_types);
1544
1545        test!(@binary BitAnd, bitand, BitAndAssign, bitand_assign, call_for_unsigned_types, call_for_signed_types);
1546        test!(@binary BitOr, bitor, BitOrAssign, bitor_assign, call_for_unsigned_types, call_for_signed_types);
1547        test!(@binary BitXor, bitxor, BitXorAssign, bitxor_assign, call_for_unsigned_types, call_for_signed_types);
1548        test!(@binary Shl, shl[checked_shl], ShlAssign, shl_assign, call_for_unsigned_types, call_for_signed_types);
1549        test!(@binary Shr, shr[checked_shr], ShrAssign, shr_assign, call_for_unsigned_types, call_for_signed_types);
1550
1551        test!(@unary Not, not, call_for_signed_types, call_for_unsigned_types);
1552        test!(@unary Neg, neg, call_for_signed_types, call_for_float_types);
1553    }
1554
1555    #[test]
1556    fn test_debug_impl() {
1557        // Ensure that Debug applies format options to the inner value.
1558        let val = U16::<LE>::new(10);
1559        assert_eq!(format!("{:?}", val), "U16(10)");
1560        assert_eq!(format!("{:03?}", val), "U16(010)");
1561        assert_eq!(format!("{:x?}", val), "U16(a)");
1562    }
1563
1564    #[test]
1565    fn test_byteorder_traits_coverage() {
1566        let val_be = U16::<BigEndian>::from_bytes([0, 1]);
1567        let val_le = U16::<LittleEndian>::from_bytes([1, 0]);
1568
1569        assert_eq!(val_be.get(), 1);
1570        assert_eq!(val_le.get(), 1);
1571
1572        // Debug
1573        assert_eq!(format!("{:?}", val_be), "U16(1)");
1574        assert_eq!(format!("{:?}", val_le), "U16(1)");
1575
1576        // PartialOrd, Ord with same type
1577        assert!(val_be >= val_be);
1578        assert!(val_be <= val_be);
1579        assert_eq!(val_be.cmp(&val_be), core::cmp::Ordering::Equal);
1580
1581        // PartialOrd with native
1582        assert!(val_be == 1u16);
1583        assert!(val_be >= 1u16);
1584
1585        // Default
1586        let default_be: U16<BigEndian> = Default::default();
1587        assert_eq!(default_be.get(), 0);
1588
1589        // I16
1590        let val_be_i16 = I16::<BigEndian>::from_bytes([0, 1]);
1591        assert_eq!(val_be_i16.get(), 1);
1592        assert_eq!(format!("{:?}", val_be_i16), "I16(1)");
1593        assert_eq!(val_be_i16.cmp(&val_be_i16), core::cmp::Ordering::Equal);
1594    }
1595}