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