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            //! The integer types: `i24`, `i40`, `i48`, `i56`, `i72` through `i120`, and
386            //! the unsigned equivalents `u24`, `u40`, ... `u120`.
387            //!
388            //! Each type is a fixed-width integer whose width is a whole number of bytes
389            //! but not one of the native widths (8, 16, 32, 64 or 128 bits). For those,
390            //! use the primitive types.
391            //!
392            //! # Layout
393            //!
394            //! Every type has the same size and alignment as the primitive that backs it
395            //! (`i24` and `u24` use `i32` and `u32`; `i40` through `i56` use 64-bit
396            //! integers; `i72` through `i120` use 128-bit integers). The unused upper
397            //! bytes are always zero. That leaves invalid bit patterns, so
398            //! `Option<i24>` is **usually** the same size as `i24`.
399            //!
400            //! # Creating values
401            //!
402            //! - [`new`](i24::new): checked, returns `None` if the value doesn't fit
403            //! - `new_truncated`: wraps like an `as` cast, never fails
404            //! - `new_unchecked`: `unsafe`, for values already known to be in range
405            //! - `from_bits`: `unsafe`, takes the raw, pre-masked bit pattern
406            //! - the constant macros in [`literal`](crate::literal): checked at
407            //!   compile time
408            //!
409            //! `get` returns the value in the backing type, sign-extended for signed
410            //! types. `to_bits` returns the raw, unextended pattern.
411            //!
412            //! # Conversions
413            //!
414            //! Every integer type in this crate, including the primitives, converts to
415            //! every other. Conversions that can never fail implement [`From`]; the rest
416            //! implement [`TryFrom`] with [`TryFromIntError`](core::num::TryFromIntError).
417            //!
418            //! # Examples
419            //!
420            //! ```
421            //! use nbyte_int::prelude::*;
422            //!
423            //! let x = i24::new(-5).unwrap();
424            //! assert_eq!(x.get(), -5);
425            //!
426            //! // out of range
427            //! assert!(i24::new(i32::MAX).is_none());
428            //! assert_eq!(i24::MAX.get(), 8_388_607);
429            //!
430            //! // same size as the backing type, and Option is free (usually - not a guarantee)
431            //! assert_eq!(size_of::<i24>(), size_of::<i32>());
432            //! assert_eq!(size_of::<Option<u40>>(), size_of::<u40>());
433            //!
434            //! // lossless conversions use `From`, the rest `TryFrom`
435            //! assert_eq!(i24::from(100_i16).get(), 100);
436            //! assert!(u24::try_from(-1_i32).is_err());
437            //! assert_eq!(u24::try_from(70_000_i64).unwrap().get(), 70_000);
438            //!
439            //! // byte order
440            //! assert_eq!(u24::new(0x010203).unwrap().to_le_bytes(), [0x03, 0x02, 0x01]);
441            //! ```
442            use super::*;
443
444            $(const _: () = {
445                let min = $size / 2;
446                let max = size_of::<$backing_ty>();
447                assert!($size == max, "invalid backing type size");
448                $(
449                assert!(
450                    $bit_count == $byte_count * 8,
451                    concat!(
452                        "invalid nbyte bit count for ",
453                        stringify!($byte_count),
454                        " bytes",
455                    )
456                );
457                assert!(min < $byte_count && $byte_count < max, "invalid nbyte int");
458                )*
459                let len = <[_]>::len(&[$($byte_count),*]);
460                // len = (min..max).len() - 1
461                // len = max - min - 1
462                assert!(len == max - min - 1, "not all byte integers implemented");
463            };
464
465            $(
466            declare_inner_struct! {
467                [<$signed_prefix $bit_count LittleEndian Inner>] @[$size] {
468                    bytes: [u8; $byte_count],
469                    zeros: [Zero; { $size - $byte_count }],
470                }
471            }
472
473            declare_inner_struct! {
474                [<$signed_prefix $bit_count BigEndian Inner>] @[$size] {
475                    zeros: [Zero; { $size - $byte_count }],
476                    bytes: [u8; $byte_count]
477                }
478            }
479
480
481            cfg_select! {
482                target_endian = "little" => {
483                    type [<$signed_prefix $bit_count Inner>] = [<$signed_prefix $bit_count LittleEndian Inner>];
484                    type [<Flipped $signed_prefix $bit_count Inner>] = [<$signed_prefix $bit_count BigEndian Inner>];
485                }
486                target_endian = "big" => {
487                    type [<$signed_prefix $bit_count Inner>] = [<$signed_prefix $bit_count BigEndian Inner>];
488                    type [<Flipped $signed_prefix $bit_count Inner>] = [<$signed_prefix $bit_count LittleEndian Inner>];
489                }
490            }
491
492
493            #[allow(non_camel_case_types)]
494            #[derive(Copy, Clone)]
495            #[repr(transparent)]
496            pub struct [<$signed_prefix:lower $bit_count>]([<$signed_prefix $bit_count Inner>]);
497
498            // Safety: the inner type is also both NoUninit and Zeroable
499            #[cfg(feature = "bytemuck")]
500            unsafe impl bytemuck::NoUninit for [<$signed_prefix:lower $bit_count>] {}
501            #[cfg(feature = "bytemuck")]
502            unsafe impl bytemuck::Zeroable for [<$signed_prefix:lower $bit_count>] {}
503
504            impl [<$signed_prefix:lower $bit_count>] {
505                const DATA_BITS_MASK: $backing_ty = (1 as $backing_ty)
506                    .strict_shl($bit_count)
507                    .strict_sub(1);
508
509                const MIN_INNER: $backing_ty = match_signedness! {
510                    match $signed_prefix {
511                        SIGNED => {
512                            {
513                                let abs_value = Self::DATA_BITS_MASK.isolate_highest_one();
514                                // in twos compliment the sign bit = -expected value
515                                // so if we negate its absolute value we get the minimum
516                                abs_value.strict_neg()
517                            }
518                        },
519                        UNSIGNED => { 0 },
520                    }
521                };
522
523                const MAX_INNER: $backing_ty = match_signedness! {
524                    match $signed_prefix {
525                        SIGNED => { Self::DATA_BITS_MASK >> 1 },
526                        UNSIGNED => { Self::DATA_BITS_MASK },
527                    }
528                };
529
530
531                pub const BITS: u32 = $bit_count;
532
533                pub const MIN: Self = Self::new(Self::MIN_INNER).unwrap();
534                pub const MAX: Self = Self::new(Self::MAX_INNER).unwrap();
535
536                #[doc = concat!(
537                    "Creates a [`", stringify!([<$signed_prefix:lower $bit_count>]), "`](struct@",
538                    stringify!([<$signed_prefix:lower $bit_count>]), ") from a `",
539                    stringify!($backing_ty), "`, returning [`None`] if the value does not fit in ",
540                    stringify!($bit_count), " bits.\n\n",
541                    "The valid range is [`MIN`](Self::MIN)`..=`[`MAX`](Self::MAX).\n\n",
542                    "This is the checked counterpart of [`new_unchecked`](Self::new_unchecked) ",
543                    "and [`new_truncated`](Self::new_truncated). It is `const`, so it can be used ",
544                    "to build constants; the type's constant macro does exactly that and panics ",
545                    "at compile time on failure.\n\n",
546                    "# Examples\n\n",
547                    "```\n",
548                    "use nbyte_int::prelude::*;\n\n",
549                    "assert_eq!(", stringify!([<$signed_prefix:lower $bit_count>]), "::new(0).map(|x| x.get()), Some(0));\n",
550                    "assert_eq!(", stringify!([<$signed_prefix:lower $bit_count>]), "::new(",
551                    stringify!([<$signed_prefix:lower $bit_count>]), "::MAX.get()).map(|x| x.get()), ",
552                    "Some(", stringify!([<$signed_prefix:lower $bit_count>]), "::MAX.get()));\n",
553                    "// one past the maximum does not fit\n",
554                    "assert!(", stringify!([<$signed_prefix:lower $bit_count>]), "::MAX.get().checked_add(1)",
555                    ".and_then(", stringify!([<$signed_prefix:lower $bit_count>]), "::new).is_none());\n",
556                    "```"
557                )]
558                #[inline(always)]
559                pub const fn new(x: $backing_ty) -> Option<Self> {
560                    match x {
561                        Self::MIN_INNER..=Self::MAX_INNER => Some(unsafe { Self::new_unchecked(x) }),
562                        _ => None
563                    }
564                }
565
566                #[doc = concat!(
567                    "Creates a [`", stringify!([<$signed_prefix:lower $bit_count>]), "`](struct@",
568                    stringify!([<$signed_prefix:lower $bit_count>]), ") from a `",
569                    stringify!($backing_ty), "` without checking that it fits in ",
570                    stringify!($bit_count), " bits.\n\n",
571                    "Prefer [`new`](Self::new) (checked) or [`new_truncated`](Self::new_truncated) ",
572                    "(wrapping) unless the range check is provably redundant and measurably matters.\n\n",
573                    "# Safety\n\n",
574                    "`x` must be within [`MIN`](Self::MIN)`..=`[`MAX`](Self::MAX), i.e. the value ",
575                    "[`new`](Self::new) would accept. Violating this is immediate UB",
576                )]
577                #[inline(always)]
578                pub const unsafe fn new_unchecked(x: $backing_ty) -> Self {
579                    unsafe {
580                        core::hint::assert_unchecked(Self::MIN_INNER <= x);
581                        core::hint::assert_unchecked(x <= Self::MAX_INNER);
582                    }
583
584                    match_signedness! {
585                        match $signed_prefix {
586                            // the sign bit and friends may be on so mask it just in case
587                            SIGNED => { Self::new_truncated(x) },
588                            // unsigned no need to mask the top bits since they are already off
589                            UNSIGNED => { unsafe { Self::from_bits(x) } },
590                        }
591                    }
592                }
593
594                #[doc = concat!(
595                    "Creates a ",
596                    $byte_count,
597                    "-byte integer from its exact raw bit pattern, bypassing all masking."
598                )]
599                ///
600                /// Unlike [`new_unchecked`](Self::new_unchecked), which takes a logical integer value
601                /// and handles any necessary sign-bit masking, this transmutes the bits directly.
602                /// Only use it when you already have a pre-masked bit pattern matching the
603                /// type's exact internal representation.
604                ///
605                /// # Safety
606                #[doc = concat!(
607                    "Only the low ",
608                    $byte_count,
609                    " bytes of `bits` may be non-zero. The upper bytes must all be zero. ",
610                    "Violating this is immediate undefined behavior (UB)."
611                )]
612                #[inline(always)]
613                pub const unsafe fn from_bits(bits: $backing_ty) -> Self {
614                    // Safety: upheld by user
615                    unsafe {
616                        core::hint::assert_unchecked(bits & const { !Self::DATA_BITS_MASK } == 0);
617                        core::mem::transmute(bits)
618                    }
619                }
620
621                #[doc = concat!(
622                    "Creates a ",
623                    $byte_count,
624                    "-byte integer by truncating the upper bits of the provided value."
625                )]
626                ///
627                /// This applies a bitmask to clear any bits outside the target bit-width.
628                /// This behaves identically to primitive narrowing casts (e.g., `u32 as u16`),
629                /// where values out of bounds wrap around.
630                ///
631                /// Unlike [`new`](Self::new), this method will not return `None` on overflow.
632                /// Unlike [`new_unchecked`](Self::new_unchecked) or [`from_bits`](Self::from_bits),
633                /// this method is always safe to call with any value because it strictly enforces
634                /// the memory layout invariants via masking.
635                #[inline(always)]
636                pub const fn new_truncated(bits: $backing_ty) -> Self {
637                    unsafe { Self::from_bits(bits & Self::DATA_BITS_MASK) }
638                }
639
640                #[doc = concat!(
641                    "Returns the raw bit pattern of the ",
642                    $byte_count,
643                    "-byte integer as its underlying backing type."
644                )]
645                ///
646                /// This returns the exact memory representation of the integer where the upper
647                /// padding bytes are guaranteed to be zero.
648                ///
649                /// **Warning on Signed Integers:** This method does *not* perform sign extension.
650                /// If you call `to_bits()` on a negative value, the returned backing primitive
651                /// will have its upper bytes set to `0`, which will change its logical value.
652                ///
653                /// To retrieve the logical value with proper sign extension, always use [`get`](Self::get) instead.
654                #[inline(always)]
655                pub const fn to_bits(self) -> $backing_ty {
656                    // Safety: self is always in the same layout as backing
657                    unsafe { core::mem::transmute(self) }
658                }
659
660                #[inline(always)]
661                const fn get_swapped_bytes_inner(self) -> [u8; $byte_count] {
662                    let bits = self.to_bits();
663                    let swapped = bits.swap_bytes();
664
665                    // Safety:
666                    // swapped just swaps the bytes and so is a valid but flipped
667                    // version of self which is what [<Flipped $byte_count IntInner>] is
668                    let swapped: [<Flipped $signed_prefix $bit_count Inner>] = unsafe {
669                        core::mem::transmute(swapped)
670                    };
671
672                    swapped.bytes
673                }
674
675                #[inline(always)]
676                pub const fn swap_bytes(self) -> Self {
677                    // this has surprisingly very good codegen
678                    // and is as good if not better than anything manual after inlining
679                    Self::from_ne_bytes(self.get_swapped_bytes_inner())
680                }
681
682                #[inline(always)]
683                pub const fn from_le(x: Self) -> Self {
684                    #[cfg(target_endian = "little")]
685                    {
686                        x
687                    }
688
689                    #[cfg(target_endian = "big")]
690                    {
691                        x.swap_bytes()
692                    }
693                }
694
695                #[inline(always)]
696                pub const fn from_be(x: Self) -> Self {
697                    #[cfg(target_endian = "big")]
698                    {
699                        x
700                    }
701
702                    #[cfg(target_endian = "little")]
703                    {
704                        x.swap_bytes()
705                    }
706                }
707
708                #[inline(always)]
709                pub const fn to_le(self) -> Self {
710                    Self::from_le(self)
711                }
712
713                #[inline(always)]
714                pub const fn to_be(self) -> Self {
715                    Self::from_be(self)
716                }
717
718                #[inline(always)]
719                pub const fn from_ne_bytes(bytes: [u8; $byte_count]) -> Self {
720                    Self([<$signed_prefix $bit_count Inner>]{
721                        zeros: [Zero::_0; { $size - $byte_count }],
722                        bytes
723                    })
724                }
725
726                #[inline(always)]
727                pub const fn from_le_bytes(bytes: [u8; $byte_count]) -> Self {
728                    Self::from_le(Self::from_ne_bytes(bytes))
729                }
730
731                #[inline(always)]
732                pub const fn from_be_bytes(bytes: [u8; $byte_count]) -> Self {
733                    Self::from_be(Self::from_ne_bytes(bytes))
734                }
735
736                #[inline(always)]
737                pub const fn to_ne_bytes(self) -> [u8; $byte_count] {
738                    self.0.bytes
739                }
740
741                #[inline(always)]
742                pub const fn to_le_bytes(self) -> [u8; $byte_count] {
743                    #[cfg(target_endian = "little")]
744                    {
745                        self.0.bytes
746                    }
747
748                    #[cfg(target_endian = "big")]
749                    {
750                        self.get_swapped_bytes_inner()
751                    }
752                }
753
754                #[inline(always)]
755                pub const fn to_be_bytes(self) -> [u8; $byte_count] {
756                    #[cfg(target_endian = "big")]
757                    {
758                        self.0.bytes
759                    }
760
761                    #[cfg(target_endian = "little")]
762                    {
763                        self.get_swapped_bytes_inner()
764                    }
765                }
766
767                #[doc = concat!(
768                    "Returns the logical value of the ",
769                    $byte_count,
770                    "-byte integer as its underlying `",
771                    stringify!($backing_ty),
772                    "` backing type."
773                )]
774                ///
775                /// For signed integers, this method performs proper sign extension. This ensures
776                /// that negative values correctly retain their negative logical value when cast
777                /// up to the wider backing primitive. For unsigned integers, the value is simply
778                /// zero-extended.
779                ///
780                /// If you need the raw, unextended memory layout (where the upper padding bytes
781                /// are strictly zeroed out regardless of sign), use [`to_bits`](Self::to_bits) instead.
782                #[inline(always)]
783                pub const fn get(self) -> $backing_ty {
784                    match_signedness! {
785                        match $signed_prefix {
786                            SIGNED => {
787                                {
788                                    const OFFSET: u32 = $backing_ty::BITS
789                                        .strict_sub(<[<$signed_prefix:lower $bit_count>]>::BITS);
790
791                                    let bits = self.to_bits();
792                                    // this aligns the integers sign bit
793                                    // to the sign bit of the backing type
794                                    // and then sign extends backwards
795                                    unsafe { bits.unchecked_shl(OFFSET).unchecked_shr(OFFSET) }
796                                }
797                            },
798                            // if the data is not signed; no special handling is required
799                            UNSIGNED => { self.to_bits() },
800                        }
801                    }
802                }
803            }
804
805            defer_impl!(@($signed_prefix) [<$signed_prefix:lower $bit_count>] as $backing_ty);
806            )*
807        )*
808        }
809
810
811        pub mod literal {
812            //! Compile-time checked literal constructors, one macro per type (`i24!`, `u40!`, ...).
813            //!
814            //! They live here instead of the crate root so they don't clutter it.
815
816            #[cfg(doc)]
817            use crate::types::*;
818
819            $($(
820                #[macro_export]
821                #[doc = concat!(
822                    "Creates a [`", stringify!([<$signed_prefix:lower $bit_count>]), "`](struct@", stringify!([<$signed_prefix:lower $bit_count>]), ") from an integer literal, ",
823                    "checked at compile time.\n\n",
824                    "Fails to compile if the literal is outside [`MIN`](", stringify!([<$signed_prefix:lower $bit_count>]), "::MIN)`..=`",
825                    "[`MAX`](", stringify!([<$signed_prefix:lower $bit_count>]), "::MAX). Only literals are accepted; for other constant ",
826                    "expressions use `const { ", stringify!([<$signed_prefix:lower $bit_count>]), "::new(expr).unwrap() }`.\n\n",
827                    "# Examples\n\n",
828                    "```\n",
829                    "use nbyte_int::const_macro::", stringify!([<$signed_prefix:lower $bit_count>]), ";\n\n",
830                    "const X: nbyte_int::types::", stringify!([<$signed_prefix:lower $bit_count>]), " = ", stringify!([<$signed_prefix:lower $bit_count>]), "!(1);\n",
831                    "assert_eq!(X.get(), 1);\n",
832                    "```\n\n",
833                    "An out-of-range literal is a compile error.",
834                )]
835                #[doc(hidden)]
836                macro_rules! [<__ $signed_prefix:lower $bit_count __>] {
837                    ($__inner_expr__: literal) => {
838                        const {
839                            match $crate::types::[<$signed_prefix:lower $bit_count>]::new($__inner_expr__) {
840                                Some(x) => x,
841                                None => panic!(concat!(
842                                    "invalid ",
843                                    stringify!([<$signed_prefix:lower $bit_count>]),
844                                    " literal `",
845                                    $__inner_expr__,
846                                    "`"
847                                ))
848                            }
849                        }
850                    }
851                }
852
853                #[doc(inline)]
854                pub use [<__ $signed_prefix:lower $bit_count __>] as [<$signed_prefix:lower $bit_count>];
855            )*)*
856        }
857        }
858    };
859}
860
861macro_rules! nbyte_int {
862    (
863        $($backing: tt @($size: expr) [$($byte_count: tt @ $bit_count: tt),+ $(,)?])*
864    ) => {
865        pastey::paste! {
866            nbyte_int_inner! {
867                $(
868                [<i $backing>] @(I $size) [$($byte_count @ $bit_count),+]
869                [<u $backing>] @(U $size) [$($byte_count @ $bit_count),+]
870                )*
871            }
872        }
873    };
874}
875
876nbyte_int! {
877    // 8 bits and 16 bits have no in between
878    32 @(4) [3 @ 24]
879    64 @(8) [5 @ 40, 6 @ 48, 7 @ 56]
880    128 @(16) [9 @ 72, 10 @ 80, 11 @ 88, 12 @ 96, 13 @ 104, 14 @ 112, 15 @ 120]
881}
882
883pub mod prelude {
884    //! Everything you normally need, in one import.
885    //!
886    //! This re-exports all the integer types from [`types`](crate::types) and
887    //! the compile-time literal macros from [`literal`](crate::literal).
888
889    pub use crate::literal::*;
890    pub use crate::types::*;
891}