Skip to main content

rend/
lib.rs

1//! # rend
2//!
3//! rend provides cross-platform, endian-aware primitives for Rust.
4//!
5//! rend does not provide cross-platform alternatives for types that are
6//! inherently cross-platform, such as `bool` and `u8`. It also does not provide
7//! cross-platform alternatives for types that have an architecture-dependent
8//! size, such as `isize` and `usize`. rend does not support custom types.
9//!
10//! rend is intended to be used to build portable types that can be shared
11//! between different architectures.
12//!
13//! ## Features
14//!
15//! - `bytecheck`: Enables support for validating types using `bytecheck`.
16//!
17//! ## Crates
18//!
19//! - `zerocopy-0_8`
20//!
21//! ## Example:
22#![doc = include_str!("../example.md")]
23#![no_std]
24#![deny(
25    future_incompatible,
26    missing_docs,
27    nonstandard_style,
28    unsafe_op_in_unsafe_fn,
29    unused,
30    warnings,
31    clippy::all,
32    clippy::missing_safety_doc,
33    clippy::undocumented_unsafe_blocks,
34    rustdoc::broken_intra_doc_links,
35    rustdoc::missing_crate_level_docs
36)]
37#![cfg_attr(all(docsrs, not(doctest)), feature(doc_cfg))]
38
39#[macro_use]
40mod common;
41#[cfg(feature = "bytecheck")]
42mod context;
43#[macro_use]
44mod traits;
45#[macro_use]
46mod util;
47
48pub mod unaligned;
49
50#[cfg(target_has_atomic = "16")]
51use core::sync::atomic::{AtomicI16, AtomicU16};
52#[cfg(target_has_atomic = "32")]
53use core::sync::atomic::{AtomicI32, AtomicU32};
54#[cfg(target_has_atomic = "64")]
55use core::sync::atomic::{AtomicI64, AtomicU64};
56use core::{
57    num::{
58        NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroU128,
59        NonZeroU16, NonZeroU32, NonZeroU64,
60    },
61    sync::atomic::Ordering,
62};
63
64// `rustfmt` keeps changing the indentation of the attributes in this macro.
65#[rustfmt::skip]
66macro_rules! define_newtype {
67    (
68        $(#[$attr:meta])*
69        $name:ident: $endian:ident $size_align:literal $prim:ty
70    ) => {
71        #[allow(non_camel_case_types)]
72        #[doc = concat!(
73            "A ",
74            endian_name!($endian),
75            "-endian `",
76            stringify!($prim),
77            "` with a guaranteed size and alignment of `",
78            stringify!($size_align),
79            "`.",
80        )]
81        $(#[$attr])*
82        #[repr(C, align($size_align))]
83        pub struct $name($prim);
84    };
85}
86
87macro_rules! define_signed_integer {
88    ($name:ident: $endian:ident $size_align:literal $prim:ident) => {
89        define_newtype!(
90            #[cfg_attr(
91                feature = "zerocopy-0_8",
92                derive(
93                    zerocopy_derive::FromBytes,
94                    zerocopy_derive::IntoBytes,
95                    zerocopy_derive::Immutable,
96                    zerocopy_derive::KnownLayout,
97                ),
98            )]
99            $name: $endian $size_align $prim
100        );
101        impl_integer!($name: $endian $prim);
102        impl_signed_integer_traits!($name: $endian $prim);
103    };
104}
105
106macro_rules! define_signed_integers {
107    ($($le:ident $be:ident: $size_align:literal $prim:ident),* $(,)?) => {
108        $(
109            define_signed_integer!($le: little $size_align $prim);
110            define_signed_integer!($be: big $size_align $prim);
111        )*
112    };
113}
114
115define_signed_integers! {
116    i16_le i16_be: 2 i16,
117    i32_le i32_be: 4 i32,
118    i64_le i64_be: 8 i64,
119    i128_le i128_be: 16 i128,
120}
121
122macro_rules! define_unsigned_integer {
123    ($name:ident: $endian:ident $size_align:literal $prim:ident) => {
124        define_newtype!(
125            #[cfg_attr(
126                feature = "zerocopy-0_8",
127                derive(
128                    zerocopy_derive::FromBytes,
129                    zerocopy_derive::IntoBytes,
130                    zerocopy_derive::Immutable,
131                    zerocopy_derive::KnownLayout,
132                ),
133            )]
134            $name: $endian $size_align $prim
135        );
136        impl_integer!($name: $endian $prim);
137        impl_unsigned_integer_traits!($name: $endian $prim);
138    }
139}
140
141macro_rules! define_unsigned_integers {
142    ($($le:ident $be:ident: $size_align:literal $prim:ident),* $(,)?) => {
143        $(
144            define_unsigned_integer!($le: little $size_align $prim);
145            define_unsigned_integer!($be: big $size_align $prim);
146        )*
147    };
148}
149
150define_unsigned_integers! {
151    u16_le u16_be: 2 u16,
152    u32_le u32_be: 4 u32,
153    u64_le u64_be: 8 u64,
154    u128_le u128_be: 16 u128,
155}
156
157macro_rules! define_float {
158    (
159        $name:ident:
160        $endian:ident $size_align:literal $prim:ty as $prim_int:ty
161    ) => {
162        define_newtype!(
163            #[cfg_attr(
164                feature = "zerocopy-0_8",
165                derive(
166                    zerocopy_derive::FromBytes,
167                    zerocopy_derive::IntoBytes,
168                    zerocopy_derive::Immutable,
169                    zerocopy_derive::KnownLayout,
170                ),
171            )]
172            $name: $endian $size_align $prim
173        );
174        impl_float!($name: $endian $prim as $prim_int);
175    };
176}
177
178macro_rules! define_floats {
179    ($(
180        $le:ident $be:ident:
181        $size_align:literal $prim:ty as $prim_int:ty
182    ),* $(,)?) => {
183        $(
184            define_float!($le: little $size_align $prim as $prim_int);
185            define_float!($be: big $size_align $prim as $prim_int);
186        )*
187    };
188}
189
190define_floats! {
191    f32_le f32_be: 4 f32 as u32,
192    f64_le f64_be: 8 f64 as u64,
193}
194
195macro_rules! define_char {
196    ($name:ident: $endian:ident) => {
197        define_newtype!(
198            #[cfg_attr(
199                feature = "zerocopy-0_8",
200                derive(
201                    // The generated impl for `zerocopy::TryFromBytes` is overly
202                    // permissive. The derive macro doesn't understand that even
203                    // though this struct only contains a `u32`, it still has a
204                    // restricted set of valid bit patterns. Because
205                    // `zerocopy::TryFromBytes` has hidden, semver-breaking
206                    // members, I can't write a manual impl. So no impl for you.
207                    //
208                    // zerocopy_derive::TryFromBytes,
209                    zerocopy_derive::IntoBytes,
210                    zerocopy_derive::Immutable,
211                    zerocopy_derive::KnownLayout,
212                ),
213            )]
214            $name: $endian 4 u32
215        );
216        impl_char!($name: $endian);
217
218        #[cfg(feature = "bytecheck")]
219        // SAFETY: `check_bytes` only returns `Ok` if the code point contained
220        // within the endian-aware `char` represents a valid `char`.
221        unsafe impl<C> bytecheck::CheckBytes<C> for $name
222        where
223            C: bytecheck::rancor::Fallible + ?Sized,
224            C::Error: bytecheck::rancor::Trace,
225            char: bytecheck::CheckBytes<C>,
226        {
227            #[inline]
228            unsafe fn check_bytes(
229                value: *const Self,
230                context: &mut C,
231            ) -> Result<(), C::Error> {
232                use bytecheck::rancor::ResultExt as _;
233
234                // SAFETY: `value` points to a `Self`, which has the same size
235                // as a `u32` and is at least as aligned as one.
236                let u = unsafe { *value.cast::<u32>() };
237                let c = swap_endian!($endian u);
238                // SAFETY: `value` points to a valid endian-aware `char` type if
239                // `c` is a valid `char`.
240                unsafe {
241                    char::check_bytes(&c as *const u32 as *const char, context)
242                        .with_trace(|| $crate::context::ValueCheckContext {
243                            inner_name: "char",
244                            outer_name: core::stringify!($name),
245                        })
246                }
247            }
248        }
249    };
250}
251
252define_char!(char_le: little);
253define_char!(char_be: big);
254
255macro_rules! define_nonzero {
256    (
257        $name:ident:
258        $endian:ident $size_align:literal $prim:ty as $prim_int:ty
259    ) => {
260        define_newtype!(
261            #[cfg_attr(
262                feature = "zerocopy-0_8",
263                derive(
264                    zerocopy_derive::TryFromBytes,
265                    zerocopy_derive::IntoBytes,
266                    zerocopy_derive::Immutable,
267                    zerocopy_derive::KnownLayout,
268                ),
269            )]
270            $name: $endian $size_align $prim
271        );
272        impl_nonzero!($name: $endian $prim as $prim_int);
273
274        #[cfg(feature = "bytecheck")]
275        // SAFETY: `check_bytes` only returns `Ok` if `value` points to a valid
276        // non-zero value, which is the only requirement for `NonZero` integers.
277        unsafe impl<C> bytecheck::CheckBytes<C> for $name
278        where
279            C: bytecheck::rancor::Fallible + ?Sized,
280            C::Error: bytecheck::rancor::Trace,
281            $prim: bytecheck::CheckBytes<C>,
282        {
283            #[inline]
284            unsafe fn check_bytes(
285                value: *const Self,
286                context: &mut C,
287            ) -> Result<(), C::Error> {
288                use bytecheck::rancor::ResultExt as _;
289
290                // SAFETY: `value` points to a `Self`, which has the same size
291                // as a `$prim` and is at least as aligned as one. Note that the
292                // bit pattern for 0 is always the same regardless of
293                // endianness.
294                unsafe {
295                    <$prim>::check_bytes(value.cast(), context)
296                        .with_trace(|| $crate::context::ValueCheckContext {
297                            inner_name: core::stringify!($prim),
298                            outer_name: core::stringify!($name),
299                        })
300                }
301            }
302        }
303    };
304}
305
306macro_rules! define_nonzeros {
307    ($(
308        $le:ident $be:ident:
309        $size_align:literal $prim:ty as $prim_int:ty
310    ),* $(,)?) => {
311        $(
312            define_nonzero!($le: little $size_align $prim as $prim_int);
313            define_nonzero!($be: big $size_align $prim as $prim_int);
314        )*
315    }
316}
317
318define_nonzeros! {
319    NonZeroI16_le NonZeroI16_be: 2 NonZeroI16 as i16,
320    NonZeroI32_le NonZeroI32_be: 4 NonZeroI32 as i32,
321    NonZeroI64_le NonZeroI64_be: 8 NonZeroI64 as i64,
322    NonZeroI128_le NonZeroI128_be: 16 NonZeroI128 as i128,
323    NonZeroU16_le NonZeroU16_be: 2 NonZeroU16 as u16,
324    NonZeroU32_le NonZeroU32_be: 4 NonZeroU32 as u32,
325    NonZeroU64_le NonZeroU64_be: 8 NonZeroU64 as u64,
326    NonZeroU128_le NonZeroU128_be: 16 NonZeroU128 as u128,
327}
328
329#[allow(dead_code)]
330const fn fetch_ordering(order: Ordering) -> Ordering {
331    match order {
332        Ordering::Relaxed => Ordering::Relaxed,
333        Ordering::Release => Ordering::Relaxed,
334        Ordering::Acquire => Ordering::Acquire,
335        Ordering::AcqRel => Ordering::Acquire,
336        Ordering::SeqCst => Ordering::SeqCst,
337        order => order,
338    }
339}
340
341#[cfg(any(
342    target_has_atomic = "16",
343    target_has_atomic = "32",
344    target_has_atomic = "64",
345))]
346macro_rules! define_atomic {
347    (
348        $name:ident:
349        $endian:ident $size_align:literal $prim:ty as $prim_int:ty
350    ) => {
351        define_newtype!(
352            #[cfg_attr(
353                feature = "zerocopy-0_8",
354                derive(
355                    zerocopy_derive::FromBytes,
356                    zerocopy_derive::IntoBytes,
357                    zerocopy_derive::KnownLayout,
358                ),
359            )]
360            $name: $endian $size_align $prim
361        );
362
363        impl $name {
364            #[doc = concat!(
365                "Returns a `",
366                stringify!($name),
367                "` containing `value`.",
368            )]
369            #[inline]
370            pub const fn new(value: $prim_int) -> Self {
371                Self(<$prim>::new(swap_endian!($endian value)))
372            }
373        }
374
375        // SAFETY: An impl of `CheckBytes` with a `check_bytes` function that is
376        // a no-op is sound for atomic integers.
377        unsafe_impl_check_bytes_noop!(for $name);
378
379        impl $name {
380            /// Stores a value into the atomic integer if the current value is
381            /// the same as the `current` value.
382            ///
383            #[doc = concat!(
384                "See [`",
385                stringify!($prim),
386                "::compare_exchange`] for more information.",
387            )]
388            #[inline]
389            pub fn compare_exchange(
390                &self,
391                current: $prim_int,
392                new: $prim_int,
393                success: Ordering,
394                failure: Ordering,
395            ) -> Result<$prim_int, $prim_int> {
396                match self.0.compare_exchange(
397                    swap_endian!($endian current),
398                    swap_endian!($endian new),
399                    success,
400                    failure,
401                ) {
402                    Ok(x) => Ok(swap_endian!($endian x)),
403                    Err(x) => Err(swap_endian!($endian x)),
404                }
405            }
406
407            /// Stores a value into the atomic integer if the current value is
408            /// the same as the `current` value.
409            ///
410            #[doc = concat!(
411                "See [`",
412                stringify!($prim),
413                "::compare_exchange_weak`] for more information.",
414            )]
415            #[inline]
416            pub fn compare_exchange_weak(
417                &self,
418                current: $prim_int,
419                new: $prim_int,
420                success: Ordering,
421                failure: Ordering,
422            ) -> Result<$prim_int, $prim_int> {
423                match self.0.compare_exchange_weak(
424                    swap_endian!($endian current),
425                    swap_endian!($endian new),
426                    success,
427                    failure,
428                ) {
429                    Ok(x) => Ok(swap_endian!($endian x)),
430                    Err(x) => Err(swap_endian!($endian x)),
431                }
432            }
433
434            /// Adds to the current value, returning the previous value.
435            ///
436            #[doc = concat!(
437                "Because addition is not an endian-agnostic operation, ",
438                "`fetch_add` is implemented in terms of [`",
439                stringify!($prim),
440                "::compare_exchange_weak`] on ",
441                opposite_endian_name!($endian),
442                "-endian targets. This may result in worse performance on ",
443                "those targets.",
444            )]
445            ///
446            #[doc = concat!(
447                "See [`",
448                stringify!($prim),
449                "::fetch_add`] for more information.",
450            )]
451            #[inline]
452            pub fn fetch_add(
453                &self,
454                val: $prim_int,
455                order: Ordering,
456            ) -> $prim_int {
457                if_native_endian!(
458                    $endian
459                    self.0.fetch_add(val, order),
460                    self.fetch_update_fast(
461                        order,
462                        fetch_ordering(order),
463                        |x| x + val,
464                    ),
465                )
466            }
467
468            /// Bitwise "and" with the current value.
469            ///
470            #[doc = concat!(
471                "See [`",
472                stringify!($prim),
473                "::fetch_and`] for more information.",
474            )]
475            #[inline]
476            pub fn fetch_and(
477                &self,
478                val: $prim_int,
479                order: Ordering,
480            ) -> $prim_int {
481                let val = swap_endian!($endian val);
482                swap_endian!($endian self.0.fetch_and(val, order))
483            }
484
485            /// Maximum with the current value.
486            ///
487            #[doc = concat!(
488                "Because maximum is not an endian-agnostic operation, ",
489                "`fetch_max` is implemented in terms of [`",
490                stringify!($prim),
491                "::compare_exchange_weak`] on ",
492                opposite_endian_name!($endian),
493                "-endian targets. This may result in worse performance on ",
494                "those targets.",
495            )]
496            ///
497            #[doc = concat!(
498                "See [`",
499                stringify!($prim),
500                "::fetch_max`] for more information.",
501            )]
502            #[inline]
503            pub fn fetch_max(
504                &self,
505                val: $prim_int,
506                order: Ordering,
507            ) -> $prim_int {
508                if_native_endian!(
509                    $endian
510                    self.0.fetch_max(val, order),
511                    self.fetch_update_fast(
512                        order,
513                        fetch_ordering(order),
514                        |x| <$prim_int>::max(x, val),
515                    ),
516                )
517            }
518
519            /// Minimum with the current value.
520            ///
521            #[doc = concat!(
522                "Because minimum is not an endian-agnostic operation, ",
523                "`fetch_min` is implemented in terms of [`",
524                stringify!($prim),
525                "::compare_exchange_weak`] on ",
526                opposite_endian_name!($endian),
527                "-endian targets. This may result in worse performance on ",
528                "those targets.",
529            )]
530            ///
531            #[doc = concat!(
532                "See [`",
533                stringify!($prim),
534                "::fetch_min`] for more information.",
535            )]
536            #[inline]
537            pub fn fetch_min(
538                &self,
539                val: $prim_int,
540                order: Ordering,
541            ) -> $prim_int {
542                if_native_endian!(
543                    $endian
544                    self.0.fetch_min(val, order),
545                    self.fetch_update_fast(
546                        order,
547                        fetch_ordering(order),
548                        |x| <$prim_int>::min(x, val),
549                    ),
550                )
551            }
552
553            /// Bitwise "nand" with the current value.
554            ///
555            #[doc = concat!(
556                "See [`",
557                stringify!($prim),
558                "::fetch_nand`] for more information.",
559            )]
560            #[inline]
561            pub fn fetch_nand(
562                &self,
563                val: $prim_int,
564                order: Ordering,
565            ) -> $prim_int {
566                let val = swap_endian!($endian val);
567                swap_endian!($endian self.0.fetch_nand(val, order))
568            }
569
570            /// Bitwise "or" with the current value.
571            ///
572            #[doc = concat!(
573                "See [`",
574                stringify!($prim),
575                "::fetch_or`] for more information.",
576            )]
577            #[inline]
578            pub fn fetch_or(
579                &self,
580                val: $prim_int,
581                order: Ordering,
582            ) -> $prim_int {
583                let val = swap_endian!($endian val);
584                swap_endian!($endian self.0.fetch_or(val, order))
585            }
586
587            /// Subtracts from the current value, returning the previous value.
588            ///
589            #[doc = concat!(
590                "Because subtraction is not an endian-agnostic operation, ",
591                "`fetch_sub` is implemented in terms of [`",
592                stringify!($prim),
593                "::compare_exchange_weak`] on ",
594                opposite_endian_name!($endian),
595                "-endian targets. This may result in worse performance on ",
596                "those targets.",
597            )]
598            ///
599            #[doc = concat!(
600                "See [`",
601                stringify!($prim),
602                "::fetch_sub`] for more information.",
603            )]
604            #[inline]
605            pub fn fetch_sub(
606                &self,
607                val: $prim_int,
608                order: Ordering,
609            ) -> $prim_int {
610                if_native_endian!(
611                    $endian
612                    self.0.fetch_sub(val, order),
613                    self.fetch_update_fast(
614                        order,
615                        fetch_ordering(order),
616                        |x| x - val,
617                    ),
618                )
619            }
620
621            #[allow(dead_code)]
622            #[inline(always)]
623            fn fetch_update_fast<F: Fn($prim_int) -> $prim_int>(
624                &self,
625                set_order: Ordering,
626                fetch_order: Ordering,
627                f: F,
628            ) -> $prim_int {
629                let mut prev = self.load(fetch_order);
630                loop {
631                    let next = f(prev);
632                    match self.compare_exchange_weak(
633                        prev,
634                        next,
635                        set_order,
636                        fetch_order,
637                    ) {
638                        Ok(x) => break x,
639                        Err(next_prev) => {
640                            prev = next_prev;
641                        }
642                    }
643                }
644            }
645
646            /// Fetches the value, and applies a function to it that returns an
647            /// optional new value. Returns a `Result` of `Ok(previous_value)`
648            /// if the function returned `Some(_)`, else `Err(previous_value)`.
649            ///
650            #[doc = concat!(
651                "See [`",
652                stringify!($prim),
653                "::fetch_update`] for more information.",
654            )]
655            #[inline]
656            pub fn fetch_update<F: FnMut($prim_int) -> Option<$prim_int>>(
657                &self,
658                set_order: Ordering,
659                fetch_order: Ordering,
660                mut f: F,
661            ) -> Result<$prim_int, $prim_int> {
662                self.0.fetch_update(set_order, fetch_order, |x| {
663                    f(swap_endian!($endian x)).map(|y| swap_endian!($endian y))
664                })
665            }
666
667            /// Bitwise "xor" with the current value.
668            ///
669            #[doc = concat!(
670                "See [`",
671                stringify!($prim),
672                "::fetch_xor`] for more information.",
673            )]
674            #[inline]
675            pub fn fetch_xor(
676                &self,
677                val: $prim_int,
678                order: Ordering,
679            ) -> $prim_int {
680                let val = swap_endian!($endian val);
681                swap_endian!($endian self.0.fetch_xor(val, order))
682            }
683
684            /// Consumes the atomic and returns the contained value.
685            ///
686            #[doc = concat!(
687                "See [`",
688                stringify!($prim),
689                "::into_inner`] for more information.",
690            )]
691            #[inline]
692            pub fn into_inner(self) -> $prim_int {
693                swap_endian!($endian self.0.into_inner())
694            }
695
696            /// Loads a value from the atomic integer.
697            ///
698            #[doc = concat!(
699                "See [`",
700                stringify!($prim),
701                "::load`] for more information.",
702            )]
703            #[inline]
704            pub fn load(&self, order: Ordering) -> $prim_int {
705                swap_endian!($endian self.0.load(order))
706            }
707
708            /// Stores a value into the atomic integer.
709            ///
710            #[doc = concat!(
711                "See [`",
712                stringify!($prim),
713                "::store`] for more information.",
714            )]
715            #[inline]
716            pub fn store(&self, val: $prim_int, order: Ordering) {
717                self.0.store(swap_endian!($endian val), order);
718            }
719
720            /// Stores a value into the atomic integer, returning the previous
721            /// value.
722            ///
723            #[doc = concat!(
724                "See [`",
725                stringify!($prim),
726                "::swap`] for more information.",
727            )]
728            #[inline]
729            pub fn swap(&self, val: $prim_int, order: Ordering) -> $prim_int {
730                let val = swap_endian!($endian val);
731                swap_endian!($endian self.0.swap(val, order))
732            }
733        }
734
735        impl core::fmt::Debug for $name {
736            #[inline]
737            fn fmt(
738                &self,
739                f: &mut core::fmt::Formatter<'_>,
740            ) -> core::fmt::Result {
741                swap_endian!($endian self.load(Ordering::Relaxed)).fmt(f)
742            }
743        }
744
745        impl Default for $name {
746            #[inline]
747            fn default() -> Self {
748                Self::new(<$prim_int>::default())
749            }
750        }
751
752        impl From<$prim_int> for $name {
753            #[inline]
754            fn from(value: $prim_int) -> Self {
755                Self::new(value)
756            }
757        }
758    }
759}
760
761#[cfg(any(
762    target_has_atomic = "16",
763    target_has_atomic = "32",
764    target_has_atomic = "64",
765))]
766macro_rules! define_atomics {
767    ($(
768        $le:ident $be:ident:
769        $size_align:literal $prim:ty as $prim_int:ty
770    ),* $(,)?) => {
771        $(
772            define_atomic!($le: little $size_align $prim as $prim_int);
773            define_atomic!($be: big $size_align $prim as $prim_int);
774        )*
775    }
776}
777
778#[cfg(target_has_atomic = "16")]
779define_atomics! {
780    AtomicI16_le AtomicI16_be: 2 AtomicI16 as i16,
781    AtomicU16_le AtomicU16_be: 2 AtomicU16 as u16,
782}
783
784#[cfg(target_has_atomic = "32")]
785define_atomics! {
786    AtomicI32_le AtomicI32_be: 4 AtomicI32 as i32,
787    AtomicU32_le AtomicU32_be: 4 AtomicU32 as u32,
788}
789
790#[cfg(target_has_atomic = "64")]
791define_atomics! {
792    AtomicI64_le AtomicI64_be: 8 AtomicI64 as i64,
793    AtomicU64_le AtomicU64_be: 8 AtomicU64 as u64,
794}
795
796#[cfg(test)]
797mod tests {
798    use core::mem::transmute;
799
800    use super::*;
801
802    #[test]
803    fn signed_integers() {
804        assert_size_align! {
805            i16_be 2 2,
806            i16_le 2 2,
807            i32_be 4 4,
808            i32_le 4 4,
809            i64_be 8 8,
810            i64_le 8 8,
811            i128_be 16 16,
812            i128_le 16 16,
813        }
814
815        unsafe {
816            // i16
817            assert_eq!(
818                [0x02, 0x01],
819                transmute::<_, [u8; 2]>(i16_le::from_native(0x0102)),
820            );
821            assert_eq!(
822                [0x01, 0x02],
823                transmute::<_, [u8; 2]>(i16_be::from_native(0x0102)),
824            );
825
826            // i32
827            assert_eq!(
828                [0x04, 0x03, 0x02, 0x01],
829                transmute::<_, [u8; 4]>(i32_le::from_native(0x01020304)),
830            );
831            assert_eq!(
832                [0x01, 0x02, 0x03, 0x04],
833                transmute::<_, [u8; 4]>(i32_be::from_native(0x01020304)),
834            );
835
836            // i64
837            assert_eq!(
838                [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01],
839                transmute::<_, [u8; 8]>(i64_le::from_native(
840                    0x0102030405060708
841                )),
842            );
843            assert_eq!(
844                [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
845                transmute::<_, [u8; 8]>(i64_be::from_native(
846                    0x0102030405060708
847                )),
848            );
849
850            // i128
851            assert_eq!(
852                [
853                    0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07,
854                    0x06, 0x05, 0x04, 0x03, 0x02, 0x01
855                ],
856                transmute::<_, [u8; 16]>(i128_le::from_native(
857                    0x0102030405060708090a0b0c0d0e0f10
858                )),
859            );
860            assert_eq!(
861                [
862                    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
863                    0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10
864                ],
865                transmute::<_, [u8; 16]>(i128_be::from_native(
866                    0x0102030405060708090a0b0c0d0e0f10
867                )),
868            );
869        }
870    }
871
872    #[test]
873    fn unsigned_integers() {
874        assert_size_align! {
875            u16_be 2 2,
876            u16_le 2 2,
877            u32_be 4 4,
878            u32_le 4 4,
879            u64_be 8 8,
880            u64_le 8 8,
881            u128_be 16 16,
882            u128_le 16 16,
883        }
884
885        unsafe {
886            // u16
887            assert_eq!(
888                [0x02, 0x01],
889                transmute::<_, [u8; 2]>(u16_le::from_native(0x0102)),
890            );
891            assert_eq!(
892                [0x01, 0x02],
893                transmute::<_, [u8; 2]>(u16_be::from_native(0x0102)),
894            );
895
896            // u32
897            assert_eq!(
898                [0x04, 0x03, 0x02, 0x01],
899                transmute::<_, [u8; 4]>(u32_le::from_native(0x01020304)),
900            );
901            assert_eq!(
902                [0x01, 0x02, 0x03, 0x04],
903                transmute::<_, [u8; 4]>(u32_be::from_native(0x01020304)),
904            );
905
906            // u64
907            assert_eq!(
908                [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01],
909                transmute::<_, [u8; 8]>(u64_le::from_native(
910                    0x0102030405060708
911                )),
912            );
913            assert_eq!(
914                [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
915                transmute::<_, [u8; 8]>(u64_be::from_native(
916                    0x0102030405060708
917                )),
918            );
919
920            // u128
921            assert_eq!(
922                [
923                    0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07,
924                    0x06, 0x05, 0x04, 0x03, 0x02, 0x01
925                ],
926                transmute::<_, [u8; 16]>(u128_le::from_native(
927                    0x0102030405060708090a0b0c0d0e0f10
928                )),
929            );
930            assert_eq!(
931                [
932                    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
933                    0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10
934                ],
935                transmute::<_, [u8; 16]>(u128_be::from_native(
936                    0x0102030405060708090a0b0c0d0e0f10
937                )),
938            );
939        }
940    }
941
942    #[test]
943    fn floats() {
944        assert_size_align! {
945            f32_be 4 4,
946            f32_le 4 4,
947            f64_be 8 8,
948            f64_le 8 8,
949        }
950
951        unsafe {
952            // f32
953            assert_eq!(
954                [0xdb, 0x0f, 0x49, 0x40],
955                transmute::<_, [u8; 4]>(f32_le::from_native(
956                    core::f32::consts::PI
957                )),
958            );
959            assert_eq!(
960                [0x40, 0x49, 0x0f, 0xdb],
961                transmute::<_, [u8; 4]>(f32_be::from_native(
962                    core::f32::consts::PI
963                )),
964            );
965
966            // f64
967            assert_eq!(
968                [0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40],
969                transmute::<_, [u8; 8]>(f64_le::from_native(
970                    core::f64::consts::PI
971                )),
972            );
973            assert_eq!(
974                [0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18],
975                transmute::<_, [u8; 8]>(f64_be::from_native(
976                    core::f64::consts::PI
977                )),
978            );
979
980            // char
981            assert_eq!(
982                [0x89, 0xf3, 0x01, 0x00],
983                transmute::<_, [u8; 4]>(char_le::from_native('🎉')),
984            );
985            assert_eq!(
986                [0x00, 0x01, 0xf3, 0x89],
987                transmute::<_, [u8; 4]>(char_be::from_native('🎉')),
988            );
989        }
990    }
991
992    #[test]
993    fn signed_non_zero() {
994        assert_size_align! {
995            NonZeroI16_le 2 2,
996            NonZeroI16_be 2 2,
997            NonZeroI32_le 4 4,
998            NonZeroI32_be 4 4,
999            NonZeroI64_le 8 8,
1000            NonZeroI64_be 8 8,
1001            NonZeroI128_le 16 16,
1002            NonZeroI128_be 16 16,
1003        }
1004
1005        unsafe {
1006            // NonZeroI16
1007            assert_eq!(
1008                [0x02, 0x01],
1009                transmute::<_, [u8; 2]>(NonZeroI16_le::new_unchecked(0x0102)),
1010            );
1011            assert_eq!(
1012                [0x01, 0x02],
1013                transmute::<_, [u8; 2]>(NonZeroI16_be::new_unchecked(0x0102)),
1014            );
1015
1016            // NonZeroI32
1017            assert_eq!(
1018                [0x04, 0x03, 0x02, 0x01],
1019                transmute::<_, [u8; 4]>(NonZeroI32_le::new_unchecked(
1020                    0x01020304
1021                )),
1022            );
1023            assert_eq!(
1024                [0x01, 0x02, 0x03, 0x04],
1025                transmute::<_, [u8; 4]>(NonZeroI32_be::new_unchecked(
1026                    0x01020304
1027                )),
1028            );
1029
1030            // NonZeroI64
1031            assert_eq!(
1032                [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01],
1033                transmute::<_, [u8; 8]>(NonZeroI64_le::new_unchecked(
1034                    0x0102030405060708
1035                )),
1036            );
1037            assert_eq!(
1038                [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
1039                transmute::<_, [u8; 8]>(NonZeroI64_be::new_unchecked(
1040                    0x0102030405060708
1041                )),
1042            );
1043
1044            // NonZeroI128
1045            assert_eq!(
1046                [
1047                    0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07,
1048                    0x06, 0x05, 0x04, 0x03, 0x02, 0x01
1049                ],
1050                transmute::<_, [u8; 16]>(NonZeroI128_le::new_unchecked(
1051                    0x0102030405060708090a0b0c0d0e0f10
1052                )),
1053            );
1054            assert_eq!(
1055                [
1056                    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
1057                    0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10
1058                ],
1059                transmute::<_, [u8; 16]>(NonZeroI128_be::new_unchecked(
1060                    0x0102030405060708090a0b0c0d0e0f10
1061                )),
1062            );
1063        }
1064    }
1065
1066    #[test]
1067    fn unsigned_non_zero() {
1068        assert_size_align! {
1069            NonZeroU16_le 2 2,
1070            NonZeroU16_be 2 2,
1071            NonZeroU32_le 4 4,
1072            NonZeroU32_be 4 4,
1073            NonZeroU64_le 8 8,
1074            NonZeroU64_be 8 8,
1075            NonZeroU128_le 16 16,
1076            NonZeroU128_be 16 16,
1077        }
1078
1079        unsafe {
1080            // NonZeroU16
1081            assert_eq!(
1082                [0x02, 0x01],
1083                transmute::<_, [u8; 2]>(NonZeroU16_le::new_unchecked(0x0102)),
1084            );
1085            assert_eq!(
1086                [0x01, 0x02],
1087                transmute::<_, [u8; 2]>(NonZeroU16_be::new_unchecked(0x0102)),
1088            );
1089
1090            // NonZeroU32
1091            assert_eq!(
1092                [0x04, 0x03, 0x02, 0x01],
1093                transmute::<_, [u8; 4]>(NonZeroU32_le::new_unchecked(
1094                    0x01020304
1095                )),
1096            );
1097            assert_eq!(
1098                [0x01, 0x02, 0x03, 0x04],
1099                transmute::<_, [u8; 4]>(NonZeroU32_be::new_unchecked(
1100                    0x01020304
1101                )),
1102            );
1103
1104            // NonZeroU64
1105            assert_eq!(
1106                [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01],
1107                transmute::<_, [u8; 8]>(NonZeroU64_le::new_unchecked(
1108                    0x0102030405060708
1109                )),
1110            );
1111            assert_eq!(
1112                [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
1113                transmute::<_, [u8; 8]>(NonZeroU64_be::new_unchecked(
1114                    0x0102030405060708
1115                )),
1116            );
1117
1118            // NonZeroU128
1119            assert_eq!(
1120                [
1121                    0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07,
1122                    0x06, 0x05, 0x04, 0x03, 0x02, 0x01
1123                ],
1124                transmute::<_, [u8; 16]>(NonZeroU128_le::new_unchecked(
1125                    0x0102030405060708090a0b0c0d0e0f10
1126                )),
1127            );
1128            assert_eq!(
1129                [
1130                    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
1131                    0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10
1132                ],
1133                transmute::<_, [u8; 16]>(NonZeroU128_be::new_unchecked(
1134                    0x0102030405060708090a0b0c0d0e0f10
1135                )),
1136            );
1137        }
1138    }
1139
1140    #[cfg(feature = "bytecheck")]
1141    #[test]
1142    fn unaligned_non_zero() {
1143        use bytecheck::{
1144            rancor::{Failure, Strategy},
1145            CheckBytes,
1146        };
1147        use unaligned::{u32_ule, NonZeroU32_ule};
1148
1149        let zero = u32_ule::from_native(0);
1150        let ptr = (&zero as *const u32_ule).cast::<NonZeroU32_ule>();
1151        let mut unit = ();
1152        let context = Strategy::<_, Failure>::wrap(&mut unit);
1153        unsafe {
1154            <NonZeroU32_ule as CheckBytes<Strategy<(), Failure>>>::check_bytes(
1155                ptr, context,
1156            )
1157            .unwrap_err();
1158        }
1159    }
1160
1161    #[cfg(target_has_atomic = "16")]
1162    #[test]
1163    fn atomics_16() {
1164        assert_size_align! {
1165            AtomicI16_le 2 2,
1166            AtomicI16_be 2 2,
1167            AtomicU16_le 2 2,
1168            AtomicU16_be 2 2,
1169        }
1170
1171        unsafe {
1172            // AtomicI16
1173            assert_eq!(
1174                [0x02, 0x01],
1175                transmute::<_, [u8; 2]>(AtomicI16_le::new(0x0102)),
1176            );
1177            assert_eq!(
1178                [0x01, 0x02],
1179                transmute::<_, [u8; 2]>(AtomicI16_be::new(0x0102)),
1180            );
1181
1182            // AtomicU16
1183            assert_eq!(
1184                [0x02, 0x01],
1185                transmute::<_, [u8; 2]>(AtomicU16_le::new(0x0102)),
1186            );
1187            assert_eq!(
1188                [0x01, 0x02],
1189                transmute::<_, [u8; 2]>(AtomicU16_be::new(0x0102)),
1190            );
1191        }
1192    }
1193
1194    #[cfg(target_has_atomic = "32")]
1195    #[test]
1196    fn atomics_32() {
1197        assert_size_align! {
1198            AtomicI32_le 4 4,
1199            AtomicI32_be 4 4,
1200            AtomicU32_le 4 4,
1201            AtomicU32_be 4 4,
1202        }
1203
1204        unsafe {
1205            // AtomicI32
1206            assert_eq!(
1207                [0x04, 0x03, 0x02, 0x01],
1208                transmute::<_, [u8; 4]>(AtomicI32_le::new(0x01020304)),
1209            );
1210            assert_eq!(
1211                [0x01, 0x02, 0x03, 0x04],
1212                transmute::<_, [u8; 4]>(AtomicI32_be::new(0x01020304)),
1213            );
1214
1215            // AtomicU32
1216            assert_eq!(
1217                [0x04, 0x03, 0x02, 0x01],
1218                transmute::<_, [u8; 4]>(AtomicU32_le::new(0x01020304)),
1219            );
1220            assert_eq!(
1221                [0x01, 0x02, 0x03, 0x04],
1222                transmute::<_, [u8; 4]>(AtomicU32_be::new(0x01020304)),
1223            );
1224        }
1225    }
1226
1227    #[cfg(target_has_atomic = "64")]
1228    #[test]
1229    fn atomics_64() {
1230        assert_size_align! {
1231            AtomicI64_le 8 8,
1232            AtomicI64_be 8 8,
1233            AtomicU64_le 8 8,
1234            AtomicU64_be 8 8,
1235        }
1236
1237        unsafe {
1238            // AtomicI64
1239            assert_eq!(
1240                [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01],
1241                transmute::<_, [u8; 8]>(AtomicI64_le::new(0x0102030405060708)),
1242            );
1243            assert_eq!(
1244                [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
1245                transmute::<_, [u8; 8]>(AtomicI64_be::new(0x0102030405060708)),
1246            );
1247
1248            // AtomicU64
1249            assert_eq!(
1250                [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01],
1251                transmute::<_, [u8; 8]>(AtomicU64_le::new(0x0102030405060708)),
1252            );
1253            assert_eq!(
1254                [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
1255                transmute::<_, [u8; 8]>(AtomicU64_be::new(0x0102030405060708)),
1256            );
1257        }
1258    }
1259}