Skip to main content

nbyte_int/
lib.rs

1#![no_std]
2#![doc = include_str!("../README.md")]
3#![allow(
4    clippy::partialeq_ne_impl,
5    clippy::non_canonical_partial_ord_impl,
6    reason = "bulk forwarding to integer methods is faster than handwriting the methods"
7)]
8
9mod conversions;
10
11#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
12#[repr(u8)]
13enum Zero {
14    _0 = 0,
15}
16
17const fn from_str_error(bad_val: &str) -> core::num::ParseIntError {
18    match i8::from_str_radix(bad_val, 10) {
19        Err(err) => err,
20        Ok(_) => unreachable!(),
21    }
22}
23
24const POSITIVE_OVERFLOW: core::num::ParseIntError = from_str_error("999");
25
26const NEGATIVE_OVERFLOW: core::num::ParseIntError = from_str_error("-999");
27
28#[cfg(feature = "bytemuck")]
29unsafe impl bytemuck::Zeroable for Zero {}
30
31#[cfg(feature = "bytemuck")]
32unsafe impl bytemuck::NoUninit for Zero {}
33
34const _: () = match (cfg!(target_endian = "big"), cfg!(target_endian = "little")) {
35    (true, false) | (false, true) => {}
36    (false, false) => panic!("unknown endianness"),
37    (true, true) => panic!("invalid endianness"),
38};
39
40macro_rules! declare_inner_struct {
41    ($name: ident @[$size: expr] { $($fields: tt)* }) => {
42        #[derive(Copy, Clone)]
43        #[repr(C, align($size))]
44        struct $name {
45            $($fields)*
46        }
47
48        // Safety: all fields are NoUninit and Zeroable and there is no padding
49        // there is no padding as that is statically asserted,
50        // and they are NoUninit and Zeroable since all the fields are either `u8` or `Zero`
51        #[cfg(feature = "bytemuck")]
52        unsafe impl bytemuck::NoUninit for $name {}
53        #[cfg(feature = "bytemuck")]
54        unsafe impl bytemuck::Zeroable for $name {}
55
56
57        const _: () = assert!(size_of::<$name>() == $size);
58    };
59}
60
61macro_rules! match_signedness {
62    (match I {
63        SIGNED => { $($tt:tt)* },
64        UNSIGNED => { $($_tt:tt)* } $(,)?
65    }) => {
66        $($tt)*
67    };
68
69    (match U {
70        SIGNED => { $($_tt:tt)* },
71        UNSIGNED => { $($tt:tt)* } $(,)?
72    }) => {
73        $($tt)*
74    };
75}
76
77macro_rules! forward_ref_op_assign {
78    (impl $imp:ident, $method:ident for $t:ty) => {
79        impl core::ops::$imp<&$t> for $t {
80            #[inline(always)]
81            fn $method(&mut self, other: &$t) {
82                core::ops::$imp::$method(self, *other);
83            }
84        }
85    };
86}
87
88macro_rules! forward_ref_binop {
89    (impl $imp:ident, $method:ident for $t:ty) => {
90        impl core::ops::$imp<$t> for &$t {
91            type Output = <$t as core::ops::$imp<$t>>::Output;
92
93            #[inline(always)]
94            fn $method(self, other: $t) -> Self::Output {
95                core::ops::$imp::$method(*self, other)
96            }
97        }
98
99        impl core::ops::$imp<&$t> for $t {
100            type Output = <$t as core::ops::$imp<$t>>::Output;
101
102            #[inline(always)]
103            fn $method(self, other: &$t) -> Self::Output {
104                core::ops::$imp::$method(self, *other)
105            }
106        }
107
108        impl core::ops::$imp<&$t> for &$t {
109            type Output = <$t as core::ops::$imp<$t>>::Output;
110
111            #[inline(always)]
112            fn $method(self, other: &$t) -> Self::Output {
113                core::ops::$imp::$method(*self, *other)
114            }
115        }
116    };
117}
118
119macro_rules! impl_wrapping_binop_inner {
120    ($(impl $imp:ident, $method:ident for ($ty:ty as $inner: ty) @ $fetch:ident)+) => {$(
121        impl core::ops::$imp for $ty {
122            type Output = $ty;
123
124            #[inline(always)]
125            fn $method(self, rhs: Self) -> Self::Output {
126                let lhs = self.$fetch();
127                let rhs = rhs.$fetch();
128                let result = pastey::paste!(<$inner>::[<wrapping_ $method>](lhs, rhs));
129                Self::new_truncated(result)
130            }
131        }
132
133        forward_ref_binop! { impl $imp, $method for $ty }
134
135        pastey::paste! {
136            impl core::ops::[<$imp Assign>] for $ty {
137                #[inline(always)]
138                fn [<$method _assign>](&mut self, rhs: Self) {
139                    *self = <Self as core::ops::$imp>::$method(*self, rhs);
140                }
141            }
142
143            forward_ref_op_assign! { impl [<$imp Assign>], [<$method _assign>] for $ty }
144        }
145    )+}
146}
147
148macro_rules! impl_wrapping_bits_binop {
149    ($(impl $imp:ident, $method:ident for $t:ty as $inner: ty)+) => {$(
150        impl_wrapping_binop_inner! { impl $imp, $method for ($t as $inner) @ to_bits }
151    )+}
152}
153
154macro_rules! impl_wrapping_inner_binop {
155    ($(impl $imp:ident, $method:ident for $t:ty as $inner: ty)+) => {$(
156        impl_wrapping_binop_inner! { impl $imp, $method for ($t as $inner) @ get }
157    )+}
158}
159
160macro_rules! handle_argument {
161    ($arg_name: ident : Self) => {
162        $arg_name.get()
163    };
164    ($arg_name: ident : &Self) => {
165        &$arg_name.get()
166    };
167    ($arg_name: ident : &mut Self) => {
168        &mut $arg_name.get()
169    };
170    ($arg_name: ident : $arg_ty: ty) => {
171        $arg_name
172    };
173}
174
175macro_rules! defer_basic {
176    (
177        $(impl ($path: path) for $ty: ident as $inner: ty {
178            $(
179            $(#[$meta: meta])*
180            fn $method: ident ($(& $([$has_ref: tt])? $(mut $([$has_mut: tt])? )?)? self $(, $arg_name: ident : [$($arg_ty: tt)*])* $(,)?) -> $ret: ty;
181            )*
182        })*
183    ) => {$(
184        impl $path for $ty {
185            $(#[inline(always)]
186            $(#[$meta])*
187            fn $method($(& $($has_ref)? $(mut $($has_mut)? )?)? self $(, $arg_name : $($arg_ty)*)*) -> $ret {
188                <$inner as $path>::$method($(& $($has_ref)? $(mut $($has_mut)? )?)? self.get() $(, handle_argument!($arg_name: $($arg_ty)*))*)
189            })*
190        }
191    )*};
192}
193
194macro_rules! defer_impl {
195    (@($_sign_prefix: ident) $ty: ident as $inner: ty) => {
196        defer_basic! {
197            impl (core::fmt::Display) for $ty as $inner {
198                fn fmt(&self, f: [&mut core::fmt::Formatter<'_>]) -> core::fmt::Result;
199            }
200
201            impl (core::fmt::Debug) for $ty as $inner {
202                fn fmt(&self, f: [&mut core::fmt::Formatter<'_>]) -> core::fmt::Result;
203            }
204
205            impl (core::cmp::PartialEq) for $ty as $inner {
206                fn eq(&self, other: [&Self]) -> bool;
207
208                fn ne(&self, other: [&Self]) -> bool;
209            }
210
211            impl (core::cmp::Eq) for $ty as $inner {}
212
213
214            impl (core::cmp::PartialOrd) for $ty as $inner {
215                fn partial_cmp(&self, other: [&Self]) -> Option<core::cmp::Ordering>;
216
217
218                fn lt(&self, other: [&Self]) -> bool;
219                fn le(&self, other: [&Self]) -> bool;
220
221                fn gt(&self, other: [&Self]) -> bool;
222                fn ge(&self, other: [&Self]) -> bool;
223            }
224
225            impl (core::cmp::Ord) for $ty as $inner {
226                fn cmp(&self, other: [&Self]) -> core::cmp::Ordering;
227            }
228        }
229
230        impl core::hash::Hash for $ty {
231            fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
232                <$inner as core::hash::Hash>::hash(
233                    // Safety: self is always in the same layout as backing
234                    unsafe { &*(self as *const Self as *const $inner) },
235                    state,
236                )
237            }
238
239            fn hash_slice<H: core::hash::Hasher>(data: &[Self], state: &mut H)
240            where
241                Self: Sized,
242            {
243                <$inner as core::hash::Hash>::hash_slice(
244                    // $ty has the same layout and everything as $inner
245                    // its inner with some restrictions so this is fine
246                    unsafe { &*(data as *const [Self] as *const [$inner]) },
247                    state,
248                )
249            }
250        }
251
252        impl_wrapping_bits_binop! {
253            impl Add, add for $ty as $inner
254            impl Sub, sub for $ty as $inner
255            impl Mul, mul for $ty as $inner
256        }
257
258        impl_wrapping_inner_binop! {
259            impl Div, div for $ty as $inner
260            impl Rem, rem for $ty as $inner
261        }
262
263        impl $ty {
264            pub const fn from_str_radix(
265                str: &str,
266                radix: u32,
267            ) -> Result<Self, core::num::ParseIntError> {
268                match <$inner>::from_str_radix(str, radix) {
269                    Err(err) => Err(err),
270                    Ok(int) => match Self::new(int) {
271                        Some(casted_int) => Ok(casted_int),
272                        // for unsigned integers this obviously gets optimized out
273                        #[allow(unused_comparisons)]
274                        None if int < 0 => Err(NEGATIVE_OVERFLOW),
275                        None => Err(POSITIVE_OVERFLOW),
276                    },
277                }
278            }
279        }
280
281        impl core::str::FromStr for $ty {
282            type Err = core::num::ParseIntError;
283
284            fn from_str(s: &str) -> Result<Self, Self::Err> {
285                Self::from_str_radix(s, 10)
286            }
287        }
288
289        #[cfg(feature = "serde")]
290        impl serde::Serialize for $ty {
291            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
292            where
293                S: serde::Serializer,
294            {
295                match serializer.is_human_readable() {
296                    true => <$inner as serde::Serialize>::serialize(&self.get(), serializer),
297                    false => <[u8; { (<$ty>::BITS / 8) as usize }] as serde::Serialize>::serialize(
298                        &self.to_be_bytes(),
299                        serializer,
300                    ),
301                }
302            }
303        }
304
305        #[cfg(feature = "serde")]
306        impl<'de> serde::Deserialize<'de> for $ty {
307            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
308            where
309                D: serde::Deserializer<'de>,
310            {
311                match deserializer.is_human_readable() {
312                    true => {
313                        let int = <$inner as serde::Deserialize>::deserialize(deserializer)?;
314                        match $ty::new(int) {
315                            Some(int) => Ok(int),
316                            None => Err(<D::Error as serde::de::Error>::custom(format_args!(
317                                "invalid {int} as {}",
318                                stringify!($ty)
319                            ))),
320                        }
321                    }
322                    false => {
323                        <[u8; { (<$ty>::BITS / 8) as usize }] as serde::Deserialize>::deserialize(
324                            deserializer,
325                        )
326                        .map(<$ty>::from_be_bytes)
327                    }
328                }
329            }
330        }
331
332        #[cfg(feature = "num-traits")]
333        impl num_traits::One for $ty {
334            #[inline(always)]
335            fn one() -> Self {
336                <Self as num_traits::ConstOne>::ONE
337            }
338
339            #[inline]
340            fn is_one(&self) -> bool {
341                *self == <Self as num_traits::ConstOne>::ONE
342            }
343        }
344
345        #[cfg(feature = "num-traits")]
346        impl num_traits::ConstOne for $ty {
347            const ONE: Self = const_macro::$ty!(1);
348        }
349
350        #[cfg(feature = "num-traits")]
351        impl num_traits::Zero for $ty {
352            #[inline(always)]
353            fn zero() -> Self {
354                <Self as num_traits::ConstZero>::ZERO
355            }
356
357            #[inline(always)]
358            fn is_zero(&self) -> bool {
359                *self == <Self as num_traits::ConstZero>::ZERO
360            }
361        }
362
363        #[cfg(feature = "num-traits")]
364        impl num_traits::ConstZero for $ty {
365            const ZERO: Self = const_macro::$ty!(0);
366        }
367
368        #[cfg(feature = "num-traits")]
369        impl num_traits::Num for $ty {
370            type FromStrRadixErr = core::num::ParseIntError;
371
372            fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
373                Self::from_str_radix(str, radix)
374            }
375        }
376    };
377}
378
379macro_rules! nbyte_int_inner {
380    (
381        $($backing_ty: ident @($signed_prefix: ident $size: expr) [$($byte_count: tt @ $bit_count: tt),+])*
382    ) => {
383        pastey::paste! {
384            pub mod types {
385                use super::*;
386
387            $(const _: () = {
388                let min = $size / 2;
389                let max = size_of::<$backing_ty>();
390                assert!($size == max, "invalid backing type size");
391                $(
392                assert!(
393                    $bit_count == $byte_count * 8,
394                    concat!(
395                        "invalid nbyte bit count for ",
396                        stringify!($byte_count),
397                        " bytes",
398                    )
399                );
400                assert!(min < $byte_count && $byte_count < max, "invalid nbyte int");
401                )*
402                let len = <[_]>::len(&[$($byte_count),*]);
403                // len = (min..max).len() - 1
404                // len = max - min - 1
405                assert!(len == max - min - 1, "not all byte integers implemented");
406            };
407
408            $(
409            declare_inner_struct! {
410                [<$signed_prefix $bit_count LittleEndian Inner>] @[$size] {
411                    bytes: [u8; $byte_count],
412                    zeros: [Zero; { $size - $byte_count }],
413                }
414            }
415
416            declare_inner_struct! {
417                [<$signed_prefix $bit_count BigEndian Inner>] @[$size] {
418                    zeros: [Zero; { $size - $byte_count }],
419                    bytes: [u8; $byte_count]
420                }
421            }
422
423
424            cfg_select! {
425                target_endian = "little" => {
426                    type [<$signed_prefix $bit_count Inner>] = [<$signed_prefix $bit_count LittleEndian Inner>];
427                    type [<Flipped $signed_prefix $bit_count Inner>] = [<$signed_prefix $bit_count BigEndian Inner>];
428                }
429                target_endian = "big" => {
430                    type [<$signed_prefix $bit_count Inner>] = [<$signed_prefix $bit_count BigEndian Inner>];
431                    type [<Flipped $signed_prefix $bit_count Inner>] = [<$signed_prefix $bit_count LittleEndian Inner>];
432                }
433            }
434
435
436            #[allow(non_camel_case_types)]
437            #[derive(Copy, Clone)]
438            #[repr(transparent)]
439            pub struct [<$signed_prefix:lower $bit_count>]([<$signed_prefix $bit_count Inner>]);
440
441            // Safety: the inner type is also both NoUninit and Zeroable
442            #[cfg(feature = "bytemuck")]
443            unsafe impl bytemuck::NoUninit for [<$signed_prefix:lower $bit_count>] {}
444            #[cfg(feature = "bytemuck")]
445            unsafe impl bytemuck::Zeroable for [<$signed_prefix:lower $bit_count>] {}
446
447            impl [<$signed_prefix:lower $bit_count>] {
448                const DATA_BITS_MASK: $backing_ty = (1 as $backing_ty)
449                    .strict_shl($bit_count)
450                    .strict_sub(1);
451
452                const MIN_INNER: $backing_ty = match_signedness! {
453                    match $signed_prefix {
454                        SIGNED => {
455                            {
456                                let abs_value = Self::DATA_BITS_MASK.isolate_highest_one();
457                                // in twos compliment the sign bit = -expected value
458                                // so if we negate its absolute value we get the minimum
459                                abs_value.strict_neg()
460                            }
461                        },
462                        UNSIGNED => { 0 },
463                    }
464                };
465
466                const MAX_INNER: $backing_ty = match_signedness! {
467                    match $signed_prefix {
468                        SIGNED => { Self::DATA_BITS_MASK >> 1 },
469                        UNSIGNED => { Self::DATA_BITS_MASK },
470                    }
471                };
472
473
474                pub const BITS: u32 = $bit_count;
475
476                pub const MIN: Self = Self::new(Self::MIN_INNER).unwrap();
477                pub const MAX: Self = Self::new(Self::MAX_INNER).unwrap();
478
479                #[doc = concat!(
480                    "Creates a [`", stringify!([<$signed_prefix:lower $bit_count>]), "`](struct@",
481                    stringify!([<$signed_prefix:lower $bit_count>]), ") from a `",
482                    stringify!($backing_ty), "`, returning [`None`] if the value does not fit in ",
483                    stringify!($bit_count), " bits.\n\n",
484                    "The valid range is [`MIN`](Self::MIN)`..=`[`MAX`](Self::MAX).\n\n",
485                    "This is the checked counterpart of [`new_unchecked`](Self::new_unchecked) ",
486                    "and [`new_truncated`](Self::new_truncated). It is `const`, so it can be used ",
487                    "to build constants; the type's constant macro does exactly that and panics ",
488                    "at compile time on failure.\n\n",
489                    "# Examples\n\n",
490                    "```\n",
491                    "use nbyte_int::prelude::*;\n\n",
492                    "assert_eq!(", stringify!([<$signed_prefix:lower $bit_count>]), "::new(0).map(|x| x.get()), Some(0));\n",
493                    "assert_eq!(", stringify!([<$signed_prefix:lower $bit_count>]), "::new(",
494                    stringify!([<$signed_prefix:lower $bit_count>]), "::MAX.get()).map(|x| x.get()), ",
495                    "Some(", stringify!([<$signed_prefix:lower $bit_count>]), "::MAX.get()));\n",
496                    "// one past the maximum does not fit\n",
497                    "assert!(", stringify!([<$signed_prefix:lower $bit_count>]), "::MAX.get().checked_add(1)",
498                    ".and_then(", stringify!([<$signed_prefix:lower $bit_count>]), "::new).is_none());\n",
499                    "```"
500                )]
501                #[inline(always)]
502                pub const fn new(x: $backing_ty) -> Option<Self> {
503                    match x {
504                        Self::MIN_INNER..=Self::MAX_INNER => Some(unsafe { Self::new_unchecked(x) }),
505                        _ => None
506                    }
507                }
508
509                #[doc = concat!(
510                    "Creates a [`", stringify!([<$signed_prefix:lower $bit_count>]), "`](struct@",
511                    stringify!([<$signed_prefix:lower $bit_count>]), ") from a `",
512                    stringify!($backing_ty), "` without checking that it fits in ",
513                    stringify!($bit_count), " bits.\n\n",
514                    "Prefer [`new`](Self::new) (checked) or [`new_truncated`](Self::new_truncated) ",
515                    "(wrapping) unless the range check is provably redundant and measurably matters.\n\n",
516                    "# Safety\n\n",
517                    "`x` must be within [`MIN`](Self::MIN)`..=`[`MAX`](Self::MAX), i.e. the value ",
518                    "[`new`](Self::new) would accept. Violating this is immediate UB",
519                )]
520                #[inline(always)]
521                pub const unsafe fn new_unchecked(x: $backing_ty) -> Self {
522                    unsafe {
523                        core::hint::assert_unchecked(Self::MIN_INNER <= x);
524                        core::hint::assert_unchecked(x <= Self::MAX_INNER);
525                    }
526
527                    match_signedness! {
528                        match $signed_prefix {
529                            // the sign bit and friends may be on so mask it just in case
530                            SIGNED => { Self::new_truncated(x) },
531                            // unsigned no need to mask the top bits since they are already off
532                            UNSIGNED => { unsafe { Self::from_bits(x) } },
533                        }
534                    }
535                }
536
537                #[doc = concat!(
538                    "Creates a ",
539                    $byte_count,
540                    "-byte integer from its exact raw bit pattern, bypassing all masking."
541                )]
542                ///
543                /// Unlike [`new_unchecked`](Self::new_unchecked), which takes a logical integer value
544                /// and handles any necessary sign-bit masking, this transmutes the bits directly.
545                /// Only use it when you already have a pre-masked bit pattern matching the
546                /// type's exact internal representation.
547                ///
548                /// # Safety
549                #[doc = concat!(
550                    "Only the low ",
551                    $byte_count,
552                    " bytes of `bits` may be non-zero. The upper bytes must all be zero. ",
553                    "Violating this is immediate undefined behavior (UB)."
554                )]
555                #[inline(always)]
556                pub const unsafe fn from_bits(bits: $backing_ty) -> Self {
557                    // Safety: upheld by user
558                    unsafe {
559                        core::hint::assert_unchecked(bits & const { !Self::DATA_BITS_MASK } == 0);
560                        core::mem::transmute(bits)
561                    }
562                }
563
564                #[doc = concat!(
565                    "Creates a ",
566                    $byte_count,
567                    "-byte integer by truncating the upper bits of the provided value."
568                )]
569                ///
570                /// This applies a bitmask to clear any bits outside the target bit-width.
571                /// This behaves identically to primitive narrowing casts (e.g., `u32 as u16`),
572                /// where values out of bounds wrap around.
573                ///
574                /// Unlike [`new`](Self::new), this method will not return `None` on overflow.
575                /// Unlike [`new_unchecked`](Self::new_unchecked) or [`from_bits`](Self::from_bits),
576                /// this method is always safe to call with any value because it strictly enforces
577                /// the memory layout invariants via masking.
578                #[inline(always)]
579                pub const fn new_truncated(bits: $backing_ty) -> Self {
580                    unsafe { Self::from_bits(bits & Self::DATA_BITS_MASK) }
581                }
582
583                #[doc = concat!(
584                    "Returns the raw bit pattern of the ",
585                    $byte_count,
586                    "-byte integer as its underlying backing type."
587                )]
588                ///
589                /// This returns the exact memory representation of the integer where the upper
590                /// padding bytes are guaranteed to be zero.
591                ///
592                /// **Warning on Signed Integers:** This method does *not* perform sign extension.
593                /// If you call `to_bits()` on a negative value, the returned backing primitive
594                /// will have its upper bytes set to `0`, which will change its logical value.
595                ///
596                /// To retrieve the logical value with proper sign extension, always use [`get`](Self::get) instead.
597                #[inline(always)]
598                pub const fn to_bits(self) -> $backing_ty {
599                    // Safety: self is always in the same layout as backing
600                    unsafe { core::mem::transmute(self) }
601                }
602
603                #[inline(always)]
604                const fn get_swapped_bytes_inner(self) -> [u8; $byte_count] {
605                    let bits = self.to_bits();
606                    let swapped = bits.swap_bytes();
607
608                    // Safety:
609                    // swapped just swaps the bytes and so is a valid but flipped
610                    // version of self which is what [<Flipped $byte_count IntInner>] is
611                    let swapped: [<Flipped $signed_prefix $bit_count Inner>] = unsafe {
612                        core::mem::transmute(swapped)
613                    };
614
615                    swapped.bytes
616                }
617
618                #[inline(always)]
619                pub const fn swap_bytes(self) -> Self {
620                    // this has surprisingly very good codegen
621                    // and is as good if not better than anything manual after inlining
622                    Self::from_ne_bytes(self.get_swapped_bytes_inner())
623                }
624
625                #[inline(always)]
626                pub const fn from_le(x: Self) -> Self {
627                    #[cfg(target_endian = "little")]
628                    {
629                        x
630                    }
631
632                    #[cfg(target_endian = "big")]
633                    {
634                        x.swap_bytes()
635                    }
636                }
637
638                #[inline(always)]
639                pub const fn from_be(x: Self) -> Self {
640                    #[cfg(target_endian = "big")]
641                    {
642                        x
643                    }
644
645                    #[cfg(target_endian = "little")]
646                    {
647                        x.swap_bytes()
648                    }
649                }
650
651                #[inline(always)]
652                pub const fn to_le(self) -> Self {
653                    Self::from_le(self)
654                }
655
656                #[inline(always)]
657                pub const fn to_be(self) -> Self {
658                    Self::from_be(self)
659                }
660
661                #[inline(always)]
662                pub const fn from_ne_bytes(bytes: [u8; $byte_count]) -> Self {
663                    Self([<$signed_prefix $bit_count Inner>]{
664                        zeros: [Zero::_0; { $size - $byte_count }],
665                        bytes
666                    })
667                }
668
669                #[inline(always)]
670                pub const fn from_le_bytes(bytes: [u8; $byte_count]) -> Self {
671                    Self::from_le(Self::from_ne_bytes(bytes))
672                }
673
674                #[inline(always)]
675                pub const fn from_be_bytes(bytes: [u8; $byte_count]) -> Self {
676                    Self::from_be(Self::from_ne_bytes(bytes))
677                }
678
679                #[inline(always)]
680                pub const fn to_ne_bytes(self) -> [u8; $byte_count] {
681                    self.0.bytes
682                }
683
684                #[inline(always)]
685                pub const fn to_le_bytes(self) -> [u8; $byte_count] {
686                    #[cfg(target_endian = "little")]
687                    {
688                        self.0.bytes
689                    }
690
691                    #[cfg(target_endian = "big")]
692                    {
693                        self.get_swapped_bytes_inner()
694                    }
695                }
696
697                #[inline(always)]
698                pub const fn to_be_bytes(self) -> [u8; $byte_count] {
699                    #[cfg(target_endian = "big")]
700                    {
701                        self.0.bytes
702                    }
703
704                    #[cfg(target_endian = "little")]
705                    {
706                        self.get_swapped_bytes_inner()
707                    }
708                }
709
710                #[doc = concat!(
711                    "Returns the logical value of the ",
712                    $byte_count,
713                    "-byte integer as its underlying `",
714                    stringify!($backing_ty),
715                    "` backing type."
716                )]
717                ///
718                /// For signed integers, this method performs proper sign extension. This ensures
719                /// that negative values correctly retain their negative logical value when cast
720                /// up to the wider backing primitive. For unsigned integers, the value is simply
721                /// zero-extended.
722                ///
723                /// If you need the raw, unextended memory layout (where the upper padding bytes
724                /// are strictly zeroed out regardless of sign), use [`to_bits`](Self::to_bits) instead.
725                #[inline(always)]
726                pub const fn get(self) -> $backing_ty {
727                    match_signedness! {
728                        match $signed_prefix {
729                            SIGNED => {
730                                {
731                                    const OFFSET: u32 = $backing_ty::BITS
732                                        .strict_sub(<[<$signed_prefix:lower $bit_count>]>::BITS);
733
734                                    let bits = self.to_bits();
735                                    // this aligns the integers sign bit
736                                    // to the sign bit of the backing type
737                                    // and then sign extends backwards
738                                    unsafe { bits.unchecked_shl(OFFSET).unchecked_shr(OFFSET) }
739                                }
740                            },
741                            // if the data is not signed; no special handling is required
742                            UNSIGNED => { self.to_bits() },
743                        }
744                    }
745                }
746            }
747
748            defer_impl!(@($signed_prefix) [<$signed_prefix:lower $bit_count>] as $backing_ty);
749            )*
750        )*
751        }
752
753
754        pub mod const_macro {
755            $($(
756                #[macro_export]
757                macro_rules! [<$signed_prefix:lower $bit_count>] {
758                    ($__inner_expr__: literal) => {
759                        const {
760                            match $crate::types::[<$signed_prefix:lower $bit_count>]::new($__inner_expr__) {
761                                Some(x) => x,
762                                None => panic!(concat!(
763                                    "invalid ",
764                                    stringify!([<$signed_prefix:lower $bit_count>]),
765                                    " literal `",
766                                    $__inner_expr__,
767                                    "`"
768                                ))
769                            }
770                        }
771                    }
772                }
773
774                pub use [<$signed_prefix:lower $bit_count>];
775            )*)*
776        }
777        }
778    };
779}
780
781macro_rules! nbyte_int {
782    (
783        $($backing: tt @($size: expr) [$($byte_count: tt @ $bit_count: tt),+ $(,)?])*
784    ) => {
785        pastey::paste! {
786            nbyte_int_inner! {
787                $(
788                [<i $backing>] @(I $size) [$($byte_count @ $bit_count),+]
789                [<u $backing>] @(U $size) [$($byte_count @ $bit_count),+]
790                )*
791            }
792
793            pub mod prelude {
794                pub use $crate::types::*;
795                pub use $crate::const_macro::*;
796            }
797        }
798    };
799}
800
801nbyte_int! {
802    // 8 bits and 16 bits have no in between
803    32 @(4) [3 @ 24]
804    64 @(8) [5 @ 40, 6 @ 48, 7 @ 56]
805    128 @(16) [9 @ 72, 10 @ 80, 11 @ 88, 12 @ 96, 13 @ 104, 14 @ 112, 15 @ 120]
806}