nbyte_int/
lib.rs

1#![no_std]
2
3#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
4#[repr(u8)]
5enum Zero {
6    _0 = 0
7}
8
9
10const fn from_str_error(bad_val: &str) -> core::num::ParseIntError {
11    match i8::from_str_radix(bad_val, 10) {
12        Err(err) => err,
13        Ok(_) => unreachable!(),
14    }
15}
16
17const POSITIVE_OVERFLOW: core::num::ParseIntError = from_str_error("9999999999999999999999999999999999999999");
18
19const NEGATIVE_OVERFLOW: core::num::ParseIntError = from_str_error("-9999999999999999999999999999999999999999");
20
21#[cfg(feature = "bytemuck")]
22unsafe impl bytemuck::Zeroable for Zero {}
23
24#[cfg(feature = "bytemuck")]
25unsafe impl bytemuck::NoUninit for Zero {}
26
27
28#[cfg(all(target_endian = "big", target_endian = "little"))]
29compile_error!("invalid endianness");
30
31#[cfg(all(not(target_endian = "big"), not(target_endian = "little")))]
32compile_error!("unknown endianness");
33
34
35macro_rules! declare_inner_struct {
36    ($name: ident @[$size: expr] { $($fields: tt)* }) => {
37        #[derive(Copy, Clone)]
38        #[repr(C, align($size))]
39        struct $name {
40            $($fields)*
41        }
42
43        // Safety: all fields are NoUninit and Zeroable and there is no padding
44        // there is no padding as that is statically asserted
45        // and they are NoUninit and Zeroable since all the fields are either `u8` or `Zero`
46        #[cfg(feature = "bytemuck")]
47        unsafe impl bytemuck::NoUninit for $name {}
48        #[cfg(feature = "bytemuck")]
49        unsafe impl bytemuck::Zeroable for $name {}
50
51
52        const _: () = assert!(size_of::<$name>() == $size);
53    };
54}
55
56macro_rules! match_signedness {
57    (match I {
58        SIGNED => { $($tt:tt)* },
59        UNSIGNED => { $($_tt:tt)* } $(,)?
60    }) => {
61        $($tt)*
62    };
63
64    (match U {
65        SIGNED => { $($_tt:tt)* },
66        UNSIGNED => { $($tt:tt)* } $(,)?
67    }) => {
68        $($tt)*
69    };
70}
71
72
73macro_rules! forward_ref_op_assign {
74    (impl $imp:ident, $method:ident for $t:ty) => {
75        impl core::ops::$imp<&$t> for $t {
76            #[inline(always)]
77            fn $method(&mut self, other: &$t) {
78                core::ops::$imp::$method(self, *other);
79            }
80        }
81    }
82}
83
84macro_rules! forward_ref_binop {
85    (impl $imp:ident, $method:ident for $t:ty) => {
86        impl core::ops::$imp<$t> for &$t {
87            type Output = <$t as core::ops::$imp<$t>>::Output;
88
89            #[inline(always)]
90            fn $method(self, other: $t) -> Self::Output {
91                core::ops::$imp::$method(*self, other)
92            }
93        }
94
95        impl core::ops::$imp<&$t> for $t {
96            type Output = <$t as core::ops::$imp<$t>>::Output;
97
98            #[inline(always)]
99            fn $method(self, other: &$t) -> Self::Output {
100                core::ops::$imp::$method(self, *other)
101            }
102        }
103
104        impl core::ops::$imp<&$t> for &$t {
105            type Output = <$t as core::ops::$imp<$t>>::Output;
106
107            #[inline(always)]
108            fn $method(self, other: &$t) -> Self::Output {
109                core::ops::$imp::$method(*self, *other)
110            }
111        }
112    }
113}
114
115
116macro_rules! impl_wrapping_binop_inner {
117    ($(impl $imp:ident, $method:ident for ($ty:ty as $inner: ty) @ $fetch:ident)+) => {$(
118        impl core::ops::$imp for $ty {
119            type Output = $ty;
120
121            #[inline(always)]
122            fn $method(self, rhs: Self) -> Self::Output {
123                let lhs = self.$fetch();
124                let rhs = rhs.$fetch();
125                let result = pastey::paste!(<$inner>::[<wrapping_ $method>](lhs, rhs));
126                Self::new_truncated(result)
127            }
128        }
129
130        forward_ref_binop! { impl $imp, $method for $ty }
131
132        pastey::paste! {
133            impl core::ops::[<$imp Assign>] for $ty {
134                #[inline(always)]
135                fn [<$method _assign>](&mut self, rhs: Self) {
136                    *self = <Self as core::ops::$imp>::$method(*self, rhs);
137                }
138            }
139
140            forward_ref_op_assign! { impl [<$imp Assign>], [<$method _assign>] for $ty }
141        }
142    )+}
143}
144
145macro_rules! impl_wrapping_bits_binop {
146    ($(impl $imp:ident, $method:ident for $t:ty as $inner: ty)+) => {$(
147        impl_wrapping_binop_inner! { impl $imp, $method for ($t as $inner) @ to_bits }
148    )+}
149}
150
151macro_rules! impl_wrapping_inner_binop {
152    ($(impl $imp:ident, $method:ident for $t:ty as $inner: ty)+) => {$(
153        impl_wrapping_binop_inner! { impl $imp, $method for ($t as $inner) @ get }
154    )+}
155}
156
157macro_rules! handle_argument {
158    ($arg_name: ident : Self) => { $arg_name.get() };
159    ($arg_name: ident : &Self) => { &$arg_name.get() };
160    ($arg_name: ident : &mut Self) => { &mut $arg_name.get() };
161    ($arg_name: ident : $arg_ty: ty) => { $arg_name };
162}
163
164macro_rules! defer_basic {
165    (
166        $(impl ($path: path) for $ty: ident as $inner: ty {
167            $(fn $method: ident ($(& $([$has_ref: tt])? $(mut $([$has_mut: tt])? )?)? self $(, $arg_name: ident : [$($arg_ty: tt)*])* $(,)?) -> $ret: ty;)*
168        })*
169    ) => {$(
170        impl $path for $ty {
171            $(#[inline(always)]
172            fn $method($(& $($has_ref)? $(mut $($has_mut)? )?)? self $(, $arg_name : $($arg_ty)*)*) -> $ret {
173                <$inner as $path>::$method($(& $($has_ref)? $(mut $($has_mut)? )?)? self.get() $(, handle_argument!($arg_name: $($arg_ty)*))*)
174            })*
175        }
176    )*};
177}
178
179macro_rules! defer_impl {
180    (@($sign_prefix: ident) $ty: ident as $inner: ty) => {
181        defer_basic! {
182            impl (core::fmt::Display) for $ty as $inner {
183                fn fmt(&self, f: [&mut core::fmt::Formatter<'_>]) -> core::fmt::Result;
184            }
185
186            impl (core::fmt::Debug) for $ty as $inner {
187                fn fmt(&self, f: [&mut core::fmt::Formatter<'_>]) -> core::fmt::Result;
188            }
189
190            impl (core::cmp::PartialEq) for $ty as $inner {
191                fn eq(&self, other: [&Self]) -> bool;
192                fn ne(&self, other: [&Self]) -> bool;
193            }
194
195            impl (core::cmp::Eq) for $ty as $inner {}
196
197
198            impl (core::cmp::PartialOrd) for $ty as $inner {
199                fn partial_cmp(&self, other: [&Self]) -> Option<core::cmp::Ordering>;
200
201
202                fn lt(&self, other: [&Self]) -> bool;
203                fn le(&self, other: [&Self]) -> bool;
204
205                fn gt(&self, other: [&Self]) -> bool;
206                fn ge(&self, other: [&Self]) -> bool;
207            }
208
209            impl (core::cmp::Ord) for $ty as $inner {
210                fn cmp(&self, other: [&Self]) -> core::cmp::Ordering;
211            }
212        }
213
214        impl core::hash::Hash for $ty {
215            fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
216                <$inner as core::hash::Hash>::hash(&self.get(), state)
217            }
218
219            fn hash_slice<H: core::hash::Hasher>(data: &[Self], state: &mut H) where Self: Sized {
220                <$inner as core::hash::Hash>::hash_slice(
221                    // $ty has the same layout and abi and everything as $inner
222                    // its inner with some restrictions so this is fine
223                    unsafe { &*(data as *const [Self] as *const [$inner]) },
224                    state
225                )
226            }
227        }
228
229        impl_wrapping_bits_binop! {
230            impl Add, add for $ty as $inner
231            impl Sub, sub for $ty as $inner
232            impl Mul, mul for $ty as $inner
233        }
234
235        impl_wrapping_inner_binop! {
236            impl Div, div for $ty as $inner
237            impl Rem, rem for $ty as $inner
238        }
239        
240        impl $ty {
241            pub const fn from_str_radix(str: &str, radix: u32) -> Result<Self, core::num::ParseIntError> {
242                match <$inner>::from_str_radix(str, radix) {
243                    Err(err) => Err(err),
244                    Ok(int) => match Self::new(int) {
245                        Some(casted_int) => Ok(casted_int),
246                        // for unsigned integers this obviously gets optimized out
247                        #[allow(unused_comparisons)]
248                        None if int < 0 => Err(NEGATIVE_OVERFLOW),
249                        None => Err(POSITIVE_OVERFLOW)
250                    }
251                }
252            }
253        }
254        
255        impl core::str::FromStr for $ty {
256            type Err = core::num::ParseIntError;
257            
258            fn from_str(s: &str) -> Result<Self, Self::Err> {
259                Self::from_str_radix(s, 10)
260            }
261        }
262        
263        
264        
265        #[cfg(feature = "serde")]
266        impl serde::Serialize for $ty {
267            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
268            where
269                S: serde::Serializer,
270            {
271                match serializer.is_human_readable() {
272                    true => <$inner as serde::Serialize>::serialize(&self.get(), serializer),
273                    false => <[u8; { (<$ty>::BITS / 8) as usize }] as serde::Serialize>::serialize(&self.to_be_bytes(), serializer)
274                }
275            }
276        }
277
278        #[cfg(feature = "serde")]
279        impl<'de> serde::Deserialize<'de> for $ty {
280            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
281            where
282                D: serde::Deserializer<'de>,
283            {
284                match deserializer.is_human_readable() {
285                    true => {
286                        let int = <$inner as serde::Deserialize>::deserialize(deserializer)?;
287                        match $ty::new(int) {
288                            Some(int) => Ok(int),
289                            None => Err(<D::Error as serde::de::Error>::custom(
290                                format_args!("invalid {int} as {}", stringify!(i24))
291                            )),
292                        }
293                    },
294                    false => {
295                        <[u8; { (<$ty>::BITS / 8) as usize }] as serde::Deserialize>::deserialize(deserializer)
296                            .map(<$ty>::from_be_bytes)
297                    }
298                }
299            }
300    }
301
302        #[cfg(feature = "num-traits")]
303        impl num_traits::One for $ty {
304            #[inline(always)]
305            fn one() -> Self {
306                <Self as num_traits::ConstOne>::ONE
307            }
308
309            #[inline]
310            fn is_one(&self) -> bool {
311                *self == <Self as num_traits::ConstOne>::ONE
312            }
313        }
314
315        #[cfg(feature = "num-traits")]
316        impl num_traits::ConstOne for $ty {
317            const ONE: Self = const_macro::$ty!(1);
318        }
319
320        #[cfg(feature = "num-traits")]
321        impl num_traits::Zero for $ty {
322            #[inline(always)]
323            fn zero() -> Self {
324                <Self as num_traits::ConstZero>::ZERO
325            }
326
327            #[inline(always)]
328            fn is_zero(&self) -> bool {
329                *self == <Self as num_traits::ConstZero>::ZERO
330            }
331        }
332
333        #[cfg(feature = "num-traits")]
334        impl num_traits::ConstZero for $ty {
335            const ZERO: Self = const_macro::$ty!(0);
336        }
337
338        #[cfg(feature = "num-traits")]
339        impl num_traits::Num for $ty {
340            type FromStrRadixErr = core::num::ParseIntError;
341
342            fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
343                Self::from_str_radix(str, radix)
344            }
345        }
346    };
347}
348
349macro_rules! nbyte_int_inner {
350    (
351        $($backing_ty: ident @($signed_prefix: ident $size: expr) [$($byte_count: tt @ $bit_count: tt),+])*
352    ) => {
353        $(
354        const _: () = {
355            let min = $size / 2;
356            let max = size_of::<$backing_ty>();
357            assert!($size == max, "invalid backing type size");
358            $(
359            assert!(
360                $bit_count == $byte_count * 8,
361                concat!(
362                    "invalid nbyte bit count for ",
363                    stringify!($byte_count),
364                    " bytes",
365                )
366            );
367            assert!(min < $byte_count && $byte_count < max, "invalid nbyte int");
368            )*
369            let len = <[_]>::len(&[$($byte_count),*]);
370            // len = (min..max).len() - 1
371            // len = max - min - 1
372            assert!(len == max - min - 1, "not all byte integers implemented");
373        };
374
375        pastey::paste! {$(
376            declare_inner_struct! {
377                [<$signed_prefix $bit_count LitteEndian Inner>] @[$size] {
378                    bytes: [u8; $byte_count],
379                    zeros: [Zero; { $size - $byte_count }],
380                }
381            }
382
383            declare_inner_struct! {
384                [<$signed_prefix $bit_count BigEndian Inner>] @[$size] {
385                    zeros: [Zero; { $size - $byte_count }],
386                    bytes: [u8; $byte_count]
387                }
388            }
389
390
391            #[cfg(target_endian = "little")]
392            type [<$signed_prefix $bit_count Inner>] = [<$signed_prefix $bit_count LitteEndian Inner>];
393
394            #[cfg(target_endian = "little")]
395            type [<Flipped $signed_prefix $bit_count Inner>] = [<$signed_prefix $bit_count BigEndian Inner>];
396
397            #[cfg(target_endian = "big")]
398            type [<$signed_prefix $bit_count Inner>] = [<$signed_prefix $bit_count BigEndian Inner>];
399
400            #[cfg(target_endian = "big")]
401            type [<Flipped $signed_prefix $bit_count Inner>] = [<$signed_prefix $bit_count LitteEndian Inner>];
402
403
404            #[allow(non_camel_case_types)]
405            #[derive(Copy, Clone)]
406            #[repr(transparent)]
407            pub struct [<$signed_prefix:lower $bit_count>]([<$signed_prefix $bit_count Inner>]);
408
409            // Safety: the inner type is also both NoUninit and Zeroable
410            #[cfg(feature = "bytemuck")]
411            unsafe impl bytemuck::NoUninit for [<$signed_prefix:lower $bit_count>] {}
412            #[cfg(feature = "bytemuck")]
413            unsafe impl bytemuck::Zeroable for [<$signed_prefix:lower $bit_count>] {}
414
415            impl [<$signed_prefix:lower $bit_count>] {
416                const DATA_BITS_MASK: $backing_ty = (1 << ($byte_count * 8)) - 1;
417
418                const MIN_INNER: $backing_ty = match_signedness! {
419                    match $signed_prefix {
420                        SIGNED => {
421                            {
422                                // FIXME (isolate_most_significant_one)
423                                // just the sign bit; but sadly this is not a negative value
424                                // in the underlying type
425                                let abs_value = (Self::DATA_BITS_MASK + 1) >> 1;
426                                // in twos compliment the sign bit = -expected value
427                                // so if we negate its absolute value we get the minimum
428
429                                -abs_value
430                            }
431                        },
432                        UNSIGNED => { 0 },
433                    }
434                };
435
436                const MAX_INNER: $backing_ty = match_signedness! {
437                    match $signed_prefix {
438                        SIGNED => { Self::DATA_BITS_MASK >> 1 },
439                        UNSIGNED => { Self::DATA_BITS_MASK },
440                    }
441                };
442
443
444                pub const BITS: u32 = $bit_count;
445                pub const MIN: Self = Self::new(Self::MIN_INNER).unwrap();
446                pub const MAX: Self = Self::new(Self::MAX_INNER).unwrap();
447
448                /// truncates the upper bytes and sets them to zero
449                #[inline(always)]
450                pub const fn new(bits: $backing_ty) -> Option<Self> {
451                    if Self::MIN_INNER <= bits && bits <= Self::MAX_INNER {
452                        return Some(match_signedness! {
453                            match $signed_prefix {
454                                // the sign bit and friends may be on
455                                // mask it just in case
456                                SIGNED => { Self::new_truncated(bits) },
457                                // unsigned no need to mask the top bits
458                                // they are already off
459                                UNSIGNED => { unsafe { Self::from_bits(bits) } },
460                            }
461                        })
462                    }
463
464                    None
465                }
466
467                /// # Safety
468                #[doc = concat!(
469                    "the number must only have the first ",
470                    $byte_count,
471                    " with zeros, the upper bytes must NOT be filled"
472                )]
473                #[inline(always)]
474                pub const unsafe fn from_bits(bits: $backing_ty) -> Self {
475                    // Safety: upheld by user
476                    unsafe { core::mem::transmute(bits) }
477                }
478
479                /// truncates the upper bytes and sets them to zero
480                #[inline(always)]
481                pub const fn new_truncated(bits: $backing_ty) -> Self {
482                    unsafe {
483                        Self::from_bits(bits & Self::DATA_BITS_MASK)
484                    }
485                }
486
487                #[inline(always)]
488                pub const fn to_bits(self) -> $backing_ty {
489                    // Safety: self is always in the same layout as backing
490                    unsafe { core::mem::transmute(self) }
491                }
492
493                #[inline(always)]
494                pub const fn swapped_bytes(self) -> [u8; $byte_count] {
495                    let bits = self.to_bits();
496                    let swapped = bits.swap_bytes();
497
498                    // Safety:
499                    // swapped just swaps the bytes and so is a valid
500                    // flipped version of self which is what
501                    // [<Flipped $byte_count IntInner>] is
502                    let swapped: [<Flipped $signed_prefix $bit_count Inner>] = unsafe {
503                        core::mem::transmute(swapped)
504                    };
505                    
506                    swapped.bytes
507                }
508
509                #[inline(always)]
510                pub const fn swap_bytes(self) -> Self {
511                    // this has surprisingly very good codegen
512                    // and is as good if not better than anything manual after inlining
513                    Self::from_ne_bytes(self.swapped_bytes())
514                }
515
516                #[inline(always)]
517                pub const fn to_le(self) -> Self {
518                    #[cfg(target_endian = "little")]
519                    {
520                        self
521                    }
522
523                    #[cfg(target_endian = "big")]
524                    {
525                        self.swap_bytes()
526                    }
527                }
528
529                #[inline(always)]
530                pub const fn to_be(self) -> Self {
531                    #[cfg(target_endian = "big")]
532                    {
533                        self
534                    }
535
536                    #[cfg(target_endian = "little")]
537                    {
538                        self.swap_bytes()
539                    }
540                }
541
542                #[inline(always)]
543                pub const fn from_ne_bytes(bytes: [u8; $byte_count]) -> Self {
544                    Self([<$signed_prefix $bit_count Inner>]{
545                        zeros: [Zero::_0; { $size - $byte_count }],
546                        bytes
547                    })
548                }
549
550                #[inline(always)]
551                pub const fn from_le_bytes(bytes: [u8; $byte_count]) -> Self {
552                    Self::from_ne_bytes(bytes).to_le()
553                }
554
555                #[inline(always)]
556                pub const fn from_be_bytes(bytes: [u8; $byte_count]) -> Self {
557                    Self::from_ne_bytes(bytes).to_be()
558                }
559
560                #[inline(always)]
561                pub const fn to_ne_bytes(self) -> [u8; $byte_count] {
562                    self.0.bytes
563                }
564
565                #[inline(always)]
566                pub const fn to_le_bytes(self) -> [u8; $byte_count] {
567                    #[cfg(target_endian = "little")]
568                    {
569                        self.0.bytes
570                    }
571
572                    #[cfg(target_endian = "big")]
573                    {
574                        self.swapped_bytes()
575                    }
576                }
577
578                #[inline(always)]
579                pub const fn to_be_bytes(self) -> [u8; $byte_count] {
580                    #[cfg(target_endian = "big")]
581                    {
582                        self.0.bytes
583                    }
584
585                    #[cfg(target_endian = "little")]
586                    {
587                        self.swapped_bytes()
588                    }
589                }
590
591                #[inline(always)]
592                pub const fn get(self) -> $backing_ty {
593                    match_signedness! {
594                        match $signed_prefix {
595                            SIGNED => {
596                                {
597                                    const OFFSET: u32 = $backing_ty::BITS - <[<$signed_prefix:lower $bit_count>]>::BITS;
598                                    // FIXME unchecked_shifts
599                                    // this aligns the integers sign bit
600                                    // to the sign bit of the backing type
601                                    // and then sign extends backwards
602                                    (self.to_bits() << OFFSET) >> OFFSET
603                                }
604                            },
605                            // if the data is not signed no special handling is required
606                            UNSIGNED => { self.to_bits() },
607                        }
608                    }
609                }
610            }
611
612            defer_impl!(@($signed_prefix) [<$signed_prefix:lower $bit_count>] as $backing_ty);
613        )*}
614
615        )*
616
617
618        pub mod const_macro {
619            pastey::paste! {$($(
620                #[macro_export]
621                macro_rules! [<$signed_prefix:lower $bit_count>] {
622                    ($__inner_expr__: expr) => {
623                        const {
624                            match $crate::[<$signed_prefix:lower $bit_count>]::new($__inner_expr__) {
625                                Some(x) => x,
626                                None => panic!(
627                                    concat!("invalid number constant")
628                                )
629                            }
630                        }
631                    }
632                }
633
634                pub use [<$signed_prefix:lower $bit_count>];
635            )*)*}
636        }
637    };
638}
639
640macro_rules! nbyte_int {
641    (
642        $($backing: tt @($size: expr) [$($byte_count: tt @ $bit_count: tt),+ $(,)?])*
643    ) => {
644        pastey::paste! {
645            nbyte_int_inner! {
646                $(
647                [<i $backing>] @(I $size) [$($byte_count @ $bit_count),+]
648                [<u $backing>] @(U $size) [$($byte_count @ $bit_count),+]
649                )*
650            }
651        }
652    };
653}
654
655nbyte_int! {
656    // 8 bits and 16 bits have no in between
657    32 @(4) [3 @ 24]
658    64 @(8) [5 @ 40, 6 @ 48, 7 @ 56]
659    128 @(16) [9 @ 72, 10 @ 80, 11 @ 88, 12 @ 96, 13 @ 104, 14 @ 112, 15 @ 120]
660}