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, doc_auto_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}
219
220define_char!(char_le: little);
221define_char!(char_be: big);
222
223macro_rules! define_nonzero {
224    (
225        $name:ident:
226        $endian:ident $size_align:literal $prim:ty as $prim_int:ty
227    ) => {
228        define_newtype!(
229            #[cfg_attr(
230                feature = "zerocopy-0_8",
231                derive(
232                    zerocopy_derive::TryFromBytes,
233                    zerocopy_derive::IntoBytes,
234                    zerocopy_derive::Immutable,
235                    zerocopy_derive::KnownLayout,
236                ),
237            )]
238            $name: $endian $size_align $prim
239        );
240        impl_nonzero!($name: $endian $prim as $prim_int);
241
242        #[cfg(feature = "bytecheck")]
243        // SAFETY: `check_bytes` only returns `Ok` if `value` points to a valid
244        // non-zero value, which is the only requirement for `NonZero` integers.
245        unsafe impl<C> bytecheck::CheckBytes<C> for $name
246        where
247            C: bytecheck::rancor::Fallible + ?Sized,
248            C::Error: bytecheck::rancor::Trace,
249            $prim: bytecheck::CheckBytes<C>,
250        {
251            #[inline]
252            unsafe fn check_bytes(
253                value: *const Self,
254                context: &mut C,
255            ) -> Result<(), C::Error> {
256                use bytecheck::rancor::ResultExt as _;
257
258                // SAFETY: `value` points to a `Self`, which has the same size
259                // as a `$prim` and is at least as aligned as one. Note that the
260                // bit pattern for 0 is always the same regardless of
261                // endianness.
262                unsafe {
263                    <$prim>::check_bytes(value.cast(), context)
264                        .with_trace(|| $crate::context::ValueCheckContext {
265                            inner_name: core::stringify!($prim),
266                            outer_name: core::stringify!($name),
267                        })
268                }
269            }
270        }
271    };
272}
273
274macro_rules! define_nonzeros {
275    ($(
276        $le:ident $be:ident:
277        $size_align:literal $prim:ty as $prim_int:ty
278    ),* $(,)?) => {
279        $(
280            define_nonzero!($le: little $size_align $prim as $prim_int);
281            define_nonzero!($be: big $size_align $prim as $prim_int);
282        )*
283    }
284}
285
286define_nonzeros! {
287    NonZeroI16_le NonZeroI16_be: 2 NonZeroI16 as i16,
288    NonZeroI32_le NonZeroI32_be: 4 NonZeroI32 as i32,
289    NonZeroI64_le NonZeroI64_be: 8 NonZeroI64 as i64,
290    NonZeroI128_le NonZeroI128_be: 16 NonZeroI128 as i128,
291    NonZeroU16_le NonZeroU16_be: 2 NonZeroU16 as u16,
292    NonZeroU32_le NonZeroU32_be: 4 NonZeroU32 as u32,
293    NonZeroU64_le NonZeroU64_be: 8 NonZeroU64 as u64,
294    NonZeroU128_le NonZeroU128_be: 16 NonZeroU128 as u128,
295}
296
297#[allow(dead_code)]
298const fn fetch_ordering(order: Ordering) -> Ordering {
299    match order {
300        Ordering::Relaxed => Ordering::Relaxed,
301        Ordering::Release => Ordering::Relaxed,
302        Ordering::Acquire => Ordering::Acquire,
303        Ordering::AcqRel => Ordering::Acquire,
304        Ordering::SeqCst => Ordering::SeqCst,
305        order => order,
306    }
307}
308
309#[cfg(any(
310    target_has_atomic = "16",
311    target_has_atomic = "32",
312    target_has_atomic = "64",
313))]
314macro_rules! define_atomic {
315    (
316        $name:ident:
317        $endian:ident $size_align:literal $prim:ty as $prim_int:ty
318    ) => {
319        define_newtype!(
320            #[cfg_attr(
321                feature = "zerocopy-0_8",
322                derive(
323                    zerocopy_derive::FromBytes,
324                    zerocopy_derive::IntoBytes,
325                    zerocopy_derive::KnownLayout,
326                ),
327            )]
328            $name: $endian $size_align $prim
329        );
330
331        impl $name {
332            #[doc = concat!(
333                "Returns a `",
334                stringify!($name),
335                "` containing `value`.",
336            )]
337            #[inline]
338            pub const fn new(value: $prim_int) -> Self {
339                Self(<$prim>::new(swap_endian!($endian value)))
340            }
341        }
342
343        // SAFETY: An impl of `CheckBytes` with a `check_bytes` function that is
344        // a no-op is sound for atomic integers.
345        unsafe_impl_check_bytes_noop!(for $name);
346
347        impl $name {
348            /// Stores a value into the atomic integer if the current value is
349            /// the same as the `current` value.
350            ///
351            #[doc = concat!(
352                "See [`",
353                stringify!($prim),
354                "::compare_exchange`] for more information.",
355            )]
356            #[inline]
357            pub fn compare_exchange(
358                &self,
359                current: $prim_int,
360                new: $prim_int,
361                success: Ordering,
362                failure: Ordering,
363            ) -> Result<$prim_int, $prim_int> {
364                match self.0.compare_exchange(
365                    swap_endian!($endian current),
366                    swap_endian!($endian new),
367                    success,
368                    failure,
369                ) {
370                    Ok(x) => Ok(swap_endian!($endian x)),
371                    Err(x) => Err(swap_endian!($endian x)),
372                }
373            }
374
375            /// Stores a value into the atomic integer if the current value is
376            /// the same as the `current` value.
377            ///
378            #[doc = concat!(
379                "See [`",
380                stringify!($prim),
381                "::compare_exchange_weak`] for more information.",
382            )]
383            #[inline]
384            pub fn compare_exchange_weak(
385                &self,
386                current: $prim_int,
387                new: $prim_int,
388                success: Ordering,
389                failure: Ordering,
390            ) -> Result<$prim_int, $prim_int> {
391                match self.0.compare_exchange_weak(
392                    swap_endian!($endian current),
393                    swap_endian!($endian new),
394                    success,
395                    failure,
396                ) {
397                    Ok(x) => Ok(swap_endian!($endian x)),
398                    Err(x) => Ok(swap_endian!($endian x)),
399                }
400            }
401
402            /// Adds to the current value, returning the previous value.
403            ///
404            #[doc = concat!(
405                "Because addition is not an endian-agnostic operation, ",
406                "`fetch_add` is implemented in terms of [`",
407                stringify!($prim),
408                "::compare_exchange_weak`] on ",
409                opposite_endian_name!($endian),
410                "-endian targets. This may result in worse performance on ",
411                "those targets.",
412            )]
413            ///
414            #[doc = concat!(
415                "See [`",
416                stringify!($prim),
417                "::fetch_add`] for more information.",
418            )]
419            #[inline]
420            pub fn fetch_add(
421                &self,
422                val: $prim_int,
423                order: Ordering,
424            ) -> $prim_int {
425                if_native_endian!(
426                    $endian
427                    self.0.fetch_add(val, order),
428                    self.fetch_update_fast(
429                        order,
430                        fetch_ordering(order),
431                        |x| x + val,
432                    ),
433                )
434            }
435
436            /// Bitwise "and" with the current value.
437            ///
438            #[doc = concat!(
439                "See [`",
440                stringify!($prim),
441                "::fetch_and`] for more information.",
442            )]
443            #[inline]
444            pub fn fetch_and(
445                &self,
446                val: $prim_int,
447                order: Ordering,
448            ) -> $prim_int {
449                let val = swap_endian!($endian val);
450                swap_endian!($endian self.0.fetch_and(val, order))
451            }
452
453            /// Maximum with the current value.
454            ///
455            #[doc = concat!(
456                "Because maximum is not an endian-agnostic operation, ",
457                "`fetch_max` is implemented in terms of [`",
458                stringify!($prim),
459                "::compare_exchange_weak`] on ",
460                opposite_endian_name!($endian),
461                "-endian targets. This may result in worse performance on ",
462                "those targets.",
463            )]
464            ///
465            #[doc = concat!(
466                "See [`",
467                stringify!($prim),
468                "::fetch_max`] for more information.",
469            )]
470            #[inline]
471            pub fn fetch_max(
472                &self,
473                val: $prim_int,
474                order: Ordering,
475            ) -> $prim_int {
476                if_native_endian!(
477                    $endian
478                    self.0.fetch_max(val, order),
479                    self.fetch_update_fast(
480                        order,
481                        fetch_ordering(order),
482                        |x| <$prim_int>::max(x, val),
483                    ),
484                )
485            }
486
487            /// Minimum with the current value.
488            ///
489            #[doc = concat!(
490                "Because minimum is not an endian-agnostic operation, ",
491                "`fetch_min` is implemented in terms of [`",
492                stringify!($prim),
493                "::compare_exchange_weak`] on ",
494                opposite_endian_name!($endian),
495                "-endian targets. This may result in worse performance on ",
496                "those targets.",
497            )]
498            ///
499            #[doc = concat!(
500                "See [`",
501                stringify!($prim),
502                "::fetch_min`] for more information.",
503            )]
504            #[inline]
505            pub fn fetch_min(
506                &self,
507                val: $prim_int,
508                order: Ordering,
509            ) -> $prim_int {
510                if_native_endian!(
511                    $endian
512                    self.0.fetch_min(val, order),
513                    self.fetch_update_fast(
514                        order,
515                        fetch_ordering(order),
516                        |x| <$prim_int>::min(x, val),
517                    ),
518                )
519            }
520
521            /// Bitwise "nand" with the current value.
522            ///
523            #[doc = concat!(
524                "See [`",
525                stringify!($prim),
526                "::fetch_nand`] for more information.",
527            )]
528            #[inline]
529            pub fn fetch_nand(
530                &self,
531                val: $prim_int,
532                order: Ordering,
533            ) -> $prim_int {
534                let val = swap_endian!($endian val);
535                swap_endian!($endian self.0.fetch_nand(val, order))
536            }
537
538            /// Bitwise "or" with the current value.
539            ///
540            #[doc = concat!(
541                "See [`",
542                stringify!($prim),
543                "::fetch_or`] for more information.",
544            )]
545            #[inline]
546            pub fn fetch_or(
547                &self,
548                val: $prim_int,
549                order: Ordering,
550            ) -> $prim_int {
551                let val = swap_endian!($endian val);
552                swap_endian!($endian self.0.fetch_or(val, order))
553            }
554
555            /// Subtracts from the current value, returning the previous value.
556            ///
557            #[doc = concat!(
558                "Because subtraction is not an endian-agnostic operation, ",
559                "`fetch_sub` is implemented in terms of [`",
560                stringify!($prim),
561                "::compare_exchange_weak`] on ",
562                opposite_endian_name!($endian),
563                "-endian targets. This may result in worse performance on ",
564                "those targets.",
565            )]
566            ///
567            #[doc = concat!(
568                "See [`",
569                stringify!($prim),
570                "::fetch_sub`] for more information.",
571            )]
572            #[inline]
573            pub fn fetch_sub(
574                &self,
575                val: $prim_int,
576                order: Ordering,
577            ) -> $prim_int {
578                if_native_endian!(
579                    $endian
580                    self.0.fetch_sub(val, order),
581                    self.fetch_update_fast(
582                        order,
583                        fetch_ordering(order),
584                        |x| x - val,
585                    ),
586                )
587            }
588
589            #[allow(dead_code)]
590            #[inline(always)]
591            fn fetch_update_fast<F: Fn($prim_int) -> $prim_int>(
592                &self,
593                set_order: Ordering,
594                fetch_order: Ordering,
595                f: F,
596            ) -> $prim_int {
597                let mut prev = swap_endian!($endian self.0.load(fetch_order));
598                loop {
599                    let next = swap_endian!($endian f(prev));
600                    match self.0.compare_exchange_weak(
601                        prev,
602                        next,
603                        set_order,
604                        fetch_order,
605                    ) {
606                        Ok(x) => break x,
607                        Err(next_prev) => {
608                            prev = swap_endian!($endian next_prev);
609                        }
610                    }
611                }
612            }
613
614            /// Fetches the value, and applies a function to it that returns an
615            /// optional new value. Returns a `Result` of `Ok(previous_value)`
616            /// if the function returned `Some(_)`, else `Err(previous_value)`.
617            ///
618            #[doc = concat!(
619                "See [`",
620                stringify!($prim),
621                "::fetch_update`] for more information.",
622            )]
623            #[inline]
624            pub fn fetch_update<F: FnMut($prim_int) -> Option<$prim_int>>(
625                &self,
626                set_order: Ordering,
627                fetch_order: Ordering,
628                mut f: F,
629            ) -> Result<$prim_int, $prim_int> {
630                self.0.fetch_update(set_order, fetch_order, |x| {
631                    f(swap_endian!($endian x)).map(|y| swap_endian!($endian y))
632                })
633            }
634
635            /// Bitwise "xor" with the current value.
636            ///
637            #[doc = concat!(
638                "See [`",
639                stringify!($prim),
640                "::fetch_xor`] for more information.",
641            )]
642            #[inline]
643            pub fn fetch_xor(
644                &self,
645                val: $prim_int,
646                order: Ordering,
647            ) -> $prim_int {
648                let val = swap_endian!($endian val);
649                swap_endian!($endian self.0.fetch_xor(val, order))
650            }
651
652            /// Consumes the atomic and returns the contained value.
653            ///
654            #[doc = concat!(
655                "See [`",
656                stringify!($prim),
657                "::into_inner`] for more information.",
658            )]
659            #[inline]
660            pub fn into_inner(self) -> $prim_int {
661                swap_endian!($endian self.0.into_inner())
662            }
663
664            /// Loads a value from the atomic integer.
665            ///
666            #[doc = concat!(
667                "See [`",
668                stringify!($prim),
669                "::load`] for more information.",
670            )]
671            #[inline]
672            pub fn load(&self, order: Ordering) -> $prim_int {
673                swap_endian!($endian self.0.load(order))
674            }
675
676            /// Stores a value into the atomic integer.
677            ///
678            #[doc = concat!(
679                "See [`",
680                stringify!($prim),
681                "::store`] for more information.",
682            )]
683            #[inline]
684            pub fn store(&self, val: $prim_int, order: Ordering) {
685                self.0.store(swap_endian!($endian val), order);
686            }
687
688            /// Stores a value into the atomic integer, returning the previous
689            /// value.
690            ///
691            #[doc = concat!(
692                "See [`",
693                stringify!($prim),
694                "::swap`] for more information.",
695            )]
696            #[inline]
697            pub fn swap(&self, val: $prim_int, order: Ordering) -> $prim_int {
698                let val = swap_endian!($endian val);
699                swap_endian!($endian self.0.swap(val, order))
700            }
701        }
702
703        impl core::fmt::Debug for $name {
704            #[inline]
705            fn fmt(
706                &self,
707                f: &mut core::fmt::Formatter<'_>,
708            ) -> core::fmt::Result {
709                swap_endian!($endian self.load(Ordering::Relaxed)).fmt(f)
710            }
711        }
712
713        impl Default for $name {
714            #[inline]
715            fn default() -> Self {
716                Self::new(<$prim_int>::default())
717            }
718        }
719
720        impl From<$prim_int> for $name {
721            #[inline]
722            fn from(value: $prim_int) -> Self {
723                Self::new(value)
724            }
725        }
726    }
727}
728
729#[cfg(any(
730    target_has_atomic = "16",
731    target_has_atomic = "32",
732    target_has_atomic = "64",
733))]
734macro_rules! define_atomics {
735    ($(
736        $le:ident $be:ident:
737        $size_align:literal $prim:ty as $prim_int:ty
738    ),* $(,)?) => {
739        $(
740            define_atomic!($le: little $size_align $prim as $prim_int);
741            define_atomic!($be: big $size_align $prim as $prim_int);
742        )*
743    }
744}
745
746#[cfg(target_has_atomic = "16")]
747define_atomics! {
748    AtomicI16_le AtomicI16_be: 2 AtomicI16 as i16,
749    AtomicU16_le AtomicU16_be: 2 AtomicU16 as u16,
750}
751
752#[cfg(target_has_atomic = "32")]
753define_atomics! {
754    AtomicI32_le AtomicI32_be: 4 AtomicI32 as i32,
755    AtomicU32_le AtomicU32_be: 4 AtomicU32 as u32,
756}
757
758#[cfg(target_has_atomic = "64")]
759define_atomics! {
760    AtomicI64_le AtomicI64_be: 8 AtomicI64 as i64,
761    AtomicU64_le AtomicU64_be: 8 AtomicU64 as u64,
762}
763
764#[cfg(test)]
765mod tests {
766    use core::mem::transmute;
767
768    use super::*;
769
770    #[test]
771    fn signed_integers() {
772        assert_size_align! {
773            i16_be 2 2,
774            i16_le 2 2,
775            i32_be 4 4,
776            i32_le 4 4,
777            i64_be 8 8,
778            i64_le 8 8,
779            i128_be 16 16,
780            i128_le 16 16,
781        }
782
783        unsafe {
784            // i16
785            assert_eq!(
786                [0x02, 0x01],
787                transmute::<_, [u8; 2]>(i16_le::from_native(0x0102)),
788            );
789            assert_eq!(
790                [0x01, 0x02],
791                transmute::<_, [u8; 2]>(i16_be::from_native(0x0102)),
792            );
793
794            // i32
795            assert_eq!(
796                [0x04, 0x03, 0x02, 0x01],
797                transmute::<_, [u8; 4]>(i32_le::from_native(0x01020304)),
798            );
799            assert_eq!(
800                [0x01, 0x02, 0x03, 0x04],
801                transmute::<_, [u8; 4]>(i32_be::from_native(0x01020304)),
802            );
803
804            // i64
805            assert_eq!(
806                [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01],
807                transmute::<_, [u8; 8]>(i64_le::from_native(
808                    0x0102030405060708
809                )),
810            );
811            assert_eq!(
812                [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
813                transmute::<_, [u8; 8]>(i64_be::from_native(
814                    0x0102030405060708
815                )),
816            );
817
818            // i128
819            assert_eq!(
820                [
821                    0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07,
822                    0x06, 0x05, 0x04, 0x03, 0x02, 0x01
823                ],
824                transmute::<_, [u8; 16]>(i128_le::from_native(
825                    0x0102030405060708090a0b0c0d0e0f10
826                )),
827            );
828            assert_eq!(
829                [
830                    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
831                    0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10
832                ],
833                transmute::<_, [u8; 16]>(i128_be::from_native(
834                    0x0102030405060708090a0b0c0d0e0f10
835                )),
836            );
837        }
838    }
839
840    #[test]
841    fn unsigned_integers() {
842        assert_size_align! {
843            u16_be 2 2,
844            u16_le 2 2,
845            u32_be 4 4,
846            u32_le 4 4,
847            u64_be 8 8,
848            u64_le 8 8,
849            u128_be 16 16,
850            u128_le 16 16,
851        }
852
853        unsafe {
854            // u16
855            assert_eq!(
856                [0x02, 0x01],
857                transmute::<_, [u8; 2]>(u16_le::from_native(0x0102)),
858            );
859            assert_eq!(
860                [0x01, 0x02],
861                transmute::<_, [u8; 2]>(u16_be::from_native(0x0102)),
862            );
863
864            // u32
865            assert_eq!(
866                [0x04, 0x03, 0x02, 0x01],
867                transmute::<_, [u8; 4]>(u32_le::from_native(0x01020304)),
868            );
869            assert_eq!(
870                [0x01, 0x02, 0x03, 0x04],
871                transmute::<_, [u8; 4]>(u32_be::from_native(0x01020304)),
872            );
873
874            // u64
875            assert_eq!(
876                [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01],
877                transmute::<_, [u8; 8]>(u64_le::from_native(
878                    0x0102030405060708
879                )),
880            );
881            assert_eq!(
882                [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
883                transmute::<_, [u8; 8]>(u64_be::from_native(
884                    0x0102030405060708
885                )),
886            );
887
888            // u128
889            assert_eq!(
890                [
891                    0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07,
892                    0x06, 0x05, 0x04, 0x03, 0x02, 0x01
893                ],
894                transmute::<_, [u8; 16]>(u128_le::from_native(
895                    0x0102030405060708090a0b0c0d0e0f10
896                )),
897            );
898            assert_eq!(
899                [
900                    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
901                    0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10
902                ],
903                transmute::<_, [u8; 16]>(u128_be::from_native(
904                    0x0102030405060708090a0b0c0d0e0f10
905                )),
906            );
907        }
908    }
909
910    #[test]
911    fn floats() {
912        assert_size_align! {
913            f32_be 4 4,
914            f32_le 4 4,
915            f64_be 8 8,
916            f64_le 8 8,
917        }
918
919        unsafe {
920            // f32
921            assert_eq!(
922                [0xdb, 0x0f, 0x49, 0x40],
923                transmute::<_, [u8; 4]>(f32_le::from_native(
924                    core::f32::consts::PI
925                )),
926            );
927            assert_eq!(
928                [0x40, 0x49, 0x0f, 0xdb],
929                transmute::<_, [u8; 4]>(f32_be::from_native(
930                    core::f32::consts::PI
931                )),
932            );
933
934            // f64
935            assert_eq!(
936                [0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40],
937                transmute::<_, [u8; 8]>(f64_le::from_native(
938                    core::f64::consts::PI
939                )),
940            );
941            assert_eq!(
942                [0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18],
943                transmute::<_, [u8; 8]>(f64_be::from_native(
944                    core::f64::consts::PI
945                )),
946            );
947
948            // char
949            assert_eq!(
950                [0x89, 0xf3, 0x01, 0x00],
951                transmute::<_, [u8; 4]>(char_le::from_native('🎉')),
952            );
953            assert_eq!(
954                [0x00, 0x01, 0xf3, 0x89],
955                transmute::<_, [u8; 4]>(char_be::from_native('🎉')),
956            );
957        }
958    }
959
960    #[test]
961    fn signed_non_zero() {
962        assert_size_align! {
963            NonZeroI16_le 2 2,
964            NonZeroI16_be 2 2,
965            NonZeroI32_le 4 4,
966            NonZeroI32_be 4 4,
967            NonZeroI64_le 8 8,
968            NonZeroI64_be 8 8,
969            NonZeroI128_le 16 16,
970            NonZeroI128_be 16 16,
971        }
972
973        unsafe {
974            // NonZeroI16
975            assert_eq!(
976                [0x02, 0x01],
977                transmute::<_, [u8; 2]>(NonZeroI16_le::new_unchecked(0x0102)),
978            );
979            assert_eq!(
980                [0x01, 0x02],
981                transmute::<_, [u8; 2]>(NonZeroI16_be::new_unchecked(0x0102)),
982            );
983
984            // NonZeroI32
985            assert_eq!(
986                [0x04, 0x03, 0x02, 0x01],
987                transmute::<_, [u8; 4]>(NonZeroI32_le::new_unchecked(
988                    0x01020304
989                )),
990            );
991            assert_eq!(
992                [0x01, 0x02, 0x03, 0x04],
993                transmute::<_, [u8; 4]>(NonZeroI32_be::new_unchecked(
994                    0x01020304
995                )),
996            );
997
998            // NonZeroI64
999            assert_eq!(
1000                [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01],
1001                transmute::<_, [u8; 8]>(NonZeroI64_le::new_unchecked(
1002                    0x0102030405060708
1003                )),
1004            );
1005            assert_eq!(
1006                [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
1007                transmute::<_, [u8; 8]>(NonZeroI64_be::new_unchecked(
1008                    0x0102030405060708
1009                )),
1010            );
1011
1012            // NonZeroI128
1013            assert_eq!(
1014                [
1015                    0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07,
1016                    0x06, 0x05, 0x04, 0x03, 0x02, 0x01
1017                ],
1018                transmute::<_, [u8; 16]>(NonZeroI128_le::new_unchecked(
1019                    0x0102030405060708090a0b0c0d0e0f10
1020                )),
1021            );
1022            assert_eq!(
1023                [
1024                    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
1025                    0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10
1026                ],
1027                transmute::<_, [u8; 16]>(NonZeroI128_be::new_unchecked(
1028                    0x0102030405060708090a0b0c0d0e0f10
1029                )),
1030            );
1031        }
1032    }
1033
1034    #[test]
1035    fn unsigned_non_zero() {
1036        assert_size_align! {
1037            NonZeroU16_le 2 2,
1038            NonZeroU16_be 2 2,
1039            NonZeroU32_le 4 4,
1040            NonZeroU32_be 4 4,
1041            NonZeroU64_le 8 8,
1042            NonZeroU64_be 8 8,
1043            NonZeroU128_le 16 16,
1044            NonZeroU128_be 16 16,
1045        }
1046
1047        unsafe {
1048            // NonZeroU16
1049            assert_eq!(
1050                [0x02, 0x01],
1051                transmute::<_, [u8; 2]>(NonZeroU16_le::new_unchecked(0x0102)),
1052            );
1053            assert_eq!(
1054                [0x01, 0x02],
1055                transmute::<_, [u8; 2]>(NonZeroU16_be::new_unchecked(0x0102)),
1056            );
1057
1058            // NonZeroU32
1059            assert_eq!(
1060                [0x04, 0x03, 0x02, 0x01],
1061                transmute::<_, [u8; 4]>(NonZeroU32_le::new_unchecked(
1062                    0x01020304
1063                )),
1064            );
1065            assert_eq!(
1066                [0x01, 0x02, 0x03, 0x04],
1067                transmute::<_, [u8; 4]>(NonZeroU32_be::new_unchecked(
1068                    0x01020304
1069                )),
1070            );
1071
1072            // NonZeroU64
1073            assert_eq!(
1074                [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01],
1075                transmute::<_, [u8; 8]>(NonZeroU64_le::new_unchecked(
1076                    0x0102030405060708
1077                )),
1078            );
1079            assert_eq!(
1080                [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
1081                transmute::<_, [u8; 8]>(NonZeroU64_be::new_unchecked(
1082                    0x0102030405060708
1083                )),
1084            );
1085
1086            // NonZeroU128
1087            assert_eq!(
1088                [
1089                    0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07,
1090                    0x06, 0x05, 0x04, 0x03, 0x02, 0x01
1091                ],
1092                transmute::<_, [u8; 16]>(NonZeroU128_le::new_unchecked(
1093                    0x0102030405060708090a0b0c0d0e0f10
1094                )),
1095            );
1096            assert_eq!(
1097                [
1098                    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
1099                    0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10
1100                ],
1101                transmute::<_, [u8; 16]>(NonZeroU128_be::new_unchecked(
1102                    0x0102030405060708090a0b0c0d0e0f10
1103                )),
1104            );
1105        }
1106    }
1107
1108    #[cfg(feature = "bytecheck")]
1109    #[test]
1110    fn unaligned_non_zero() {
1111        use bytecheck::{
1112            rancor::{Failure, Strategy},
1113            CheckBytes,
1114        };
1115        use unaligned::{u32_ule, NonZeroU32_ule};
1116
1117        let zero = u32_ule::from_native(0);
1118        let ptr = (&zero as *const u32_ule).cast::<NonZeroU32_ule>();
1119        let mut unit = ();
1120        let context = Strategy::<_, Failure>::wrap(&mut unit);
1121        unsafe {
1122            <NonZeroU32_ule as CheckBytes<Strategy<(), Failure>>>::check_bytes(
1123                ptr, context,
1124            )
1125            .unwrap_err();
1126        }
1127    }
1128
1129    #[cfg(target_has_atomic = "16")]
1130    #[test]
1131    fn atomics_16() {
1132        assert_size_align! {
1133            AtomicI16_le 2 2,
1134            AtomicI16_be 2 2,
1135            AtomicU16_le 2 2,
1136            AtomicU16_be 2 2,
1137        }
1138
1139        unsafe {
1140            // AtomicI16
1141            assert_eq!(
1142                [0x02, 0x01],
1143                transmute::<_, [u8; 2]>(AtomicI16_le::new(0x0102)),
1144            );
1145            assert_eq!(
1146                [0x01, 0x02],
1147                transmute::<_, [u8; 2]>(AtomicI16_be::new(0x0102)),
1148            );
1149
1150            // AtomicU16
1151            assert_eq!(
1152                [0x02, 0x01],
1153                transmute::<_, [u8; 2]>(AtomicU16_le::new(0x0102)),
1154            );
1155            assert_eq!(
1156                [0x01, 0x02],
1157                transmute::<_, [u8; 2]>(AtomicU16_be::new(0x0102)),
1158            );
1159        }
1160    }
1161
1162    #[cfg(target_has_atomic = "32")]
1163    #[test]
1164    fn atomics_32() {
1165        assert_size_align! {
1166            AtomicI32_le 4 4,
1167            AtomicI32_be 4 4,
1168            AtomicU32_le 4 4,
1169            AtomicU32_be 4 4,
1170        }
1171
1172        unsafe {
1173            // AtomicI32
1174            assert_eq!(
1175                [0x04, 0x03, 0x02, 0x01],
1176                transmute::<_, [u8; 4]>(AtomicI32_le::new(0x01020304)),
1177            );
1178            assert_eq!(
1179                [0x01, 0x02, 0x03, 0x04],
1180                transmute::<_, [u8; 4]>(AtomicI32_be::new(0x01020304)),
1181            );
1182
1183            // AtomicU32
1184            assert_eq!(
1185                [0x04, 0x03, 0x02, 0x01],
1186                transmute::<_, [u8; 4]>(AtomicU32_le::new(0x01020304)),
1187            );
1188            assert_eq!(
1189                [0x01, 0x02, 0x03, 0x04],
1190                transmute::<_, [u8; 4]>(AtomicU32_be::new(0x01020304)),
1191            );
1192        }
1193    }
1194
1195    #[cfg(target_has_atomic = "64")]
1196    #[test]
1197    fn atomics_64() {
1198        assert_size_align! {
1199            AtomicI64_le 8 8,
1200            AtomicI64_be 8 8,
1201            AtomicU64_le 8 8,
1202            AtomicU64_be 8 8,
1203        }
1204
1205        unsafe {
1206            // AtomicI64
1207            assert_eq!(
1208                [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01],
1209                transmute::<_, [u8; 8]>(AtomicI64_le::new(0x0102030405060708)),
1210            );
1211            assert_eq!(
1212                [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
1213                transmute::<_, [u8; 8]>(AtomicI64_be::new(0x0102030405060708)),
1214            );
1215
1216            // AtomicU64
1217            assert_eq!(
1218                [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01],
1219                transmute::<_, [u8; 8]>(AtomicU64_le::new(0x0102030405060708)),
1220            );
1221            assert_eq!(
1222                [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
1223                transmute::<_, [u8; 8]>(AtomicU64_be::new(0x0102030405060708)),
1224            );
1225        }
1226    }
1227}