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        #[cfg(feature = "num-traits")]
241        impl num_traits::One for $ty {
242            #[inline(always)]
243            fn one() -> Self {
244                <Self as num_traits::ConstOne>::ONE
245            }
246
247            #[inline]
248            fn is_one(&self) -> bool {
249                *self == <Self as num_traits::ConstOne>::ONE
250            }
251        }
252
253        #[cfg(feature = "num-traits")]
254        impl num_traits::ConstOne for $ty {
255            const ONE: Self = const_macro::$ty!(1);
256        }
257
258        #[cfg(feature = "num-traits")]
259        impl num_traits::Zero for $ty {
260            #[inline(always)]
261            fn zero() -> Self {
262                <Self as num_traits::ConstZero>::ZERO
263            }
264
265            #[inline(always)]
266            fn is_zero(&self) -> bool {
267                *self == <Self as num_traits::ConstZero>::ZERO
268            }
269        }
270
271        #[cfg(feature = "num-traits")]
272        impl num_traits::ConstZero for $ty {
273            const ZERO: Self = const_macro::$ty!(0);
274        }
275
276        #[cfg(feature = "num-traits")]
277        impl num_traits::Num for $ty {
278            type FromStrRadixErr = core::num::ParseIntError;
279
280            fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
281                <$inner>::from_str_radix(str, radix).and_then(|int| {
282                    match Self::new(int) {
283                        Some(casted_int) => Ok(casted_int),
284                        // for unsigned integers this obviously gets optimized out
285                        #[allow(unused_comparisons)]
286                        None if int < 0 => Err(NEGATIVE_OVERFLOW),
287                        None => Err(POSITIVE_OVERFLOW)
288                    }
289                })
290            }
291        }
292
293
294    };
295}
296
297macro_rules! nbyte_int_inner {
298    (
299        $($backing_ty: ident @($signed_prefix: ident $size: expr) [$($byte_count: tt @ $bit_count: tt),+])*
300    ) => {
301        $(
302        const _: () = {
303            let min = $size / 2;
304            let max = size_of::<$backing_ty>();
305            assert!($size == max, "invalid backing type size");
306            $(
307            assert!(
308                $bit_count == $byte_count * 8,
309                concat!(
310                    "invalid nbyte bit count for ",
311                    stringify!($byte_count),
312                    " bytes",
313                )
314            );
315            assert!(min < $byte_count && $byte_count < max, "invalid nbyte int");
316            )*
317            let len = <[_]>::len(&[$($byte_count),*]);
318            // len = (min..max).len() - 1
319            // len = max - min - 1
320            assert!(len == max - min - 1, "not all byte integers implemented");
321        };
322
323        pastey::paste! {$(
324            declare_inner_struct! {
325                [<$signed_prefix $bit_count LitteEndian Inner>] @[$size] {
326                    bytes: [u8; $byte_count],
327                    zeros: [Zero; { $size - $byte_count }],
328                }
329            }
330
331            declare_inner_struct! {
332                [<$signed_prefix $bit_count BigEndian Inner>] @[$size] {
333                    zeros: [Zero; { $size - $byte_count }],
334                    bytes: [u8; $byte_count]
335                }
336            }
337
338
339            #[cfg(target_endian = "little")]
340            type [<$signed_prefix $bit_count Inner>] = [<$signed_prefix $bit_count LitteEndian Inner>];
341
342            #[cfg(target_endian = "little")]
343            type [<Flipped $signed_prefix $bit_count Inner>] = [<$signed_prefix $bit_count BigEndian Inner>];
344
345            #[cfg(target_endian = "big")]
346            type [<$signed_prefix $bit_count Inner>] = [<$signed_prefix $bit_count BigEndian Inner>];
347
348            #[cfg(target_endian = "big")]
349            type [<Flipped $signed_prefix $bit_count Inner>] = [<$signed_prefix $bit_count LitteEndian Inner>];
350
351
352            #[allow(non_camel_case_types)]
353            #[derive(Copy, Clone)]
354            #[repr(transparent)]
355            pub struct [<$signed_prefix:lower $bit_count>]([<$signed_prefix $bit_count Inner>]);
356
357            // Safety: the inner type is also both NoUninit and Zeroable
358            #[cfg(feature = "bytemuck")]
359            unsafe impl bytemuck::NoUninit for [<$signed_prefix:lower $bit_count>] {}
360            #[cfg(feature = "bytemuck")]
361            unsafe impl bytemuck::Zeroable for [<$signed_prefix:lower $bit_count>] {}
362
363            impl [<$signed_prefix:lower $bit_count>] {
364                const DATA_BITS_MASK: $backing_ty = (1 << ($byte_count * 8)) - 1;
365
366                const MIN_INNER: $backing_ty = match_signedness! {
367                    match $signed_prefix {
368                        SIGNED => {
369                            {
370                                // FIXME (isolate_most_significant_one)
371                                // just the sign bit; but sadly this is not a negative value
372                                // in the underlying type
373                                let abs_value = (Self::DATA_BITS_MASK + 1) >> 1;
374                                // in twos compliment the sign bit = -expected value
375                                // so if we negate its absolute value we get the minimum
376
377                                -abs_value
378                            }
379                        },
380                        UNSIGNED => { 0 },
381                    }
382                };
383
384                const MAX_INNER: $backing_ty = match_signedness! {
385                    match $signed_prefix {
386                        SIGNED => { Self::DATA_BITS_MASK >> 1 },
387                        UNSIGNED => { Self::DATA_BITS_MASK },
388                    }
389                };
390
391
392                pub const BITS: u32 = $bit_count;
393                pub const MIN: Self = Self::new(Self::MIN_INNER).unwrap();
394                pub const MAX: Self = Self::new(Self::MAX_INNER).unwrap();
395
396                /// truncates the upper bytes and sets them to zero
397                #[inline(always)]
398                pub const fn new(bits: $backing_ty) -> Option<Self> {
399                    if Self::MIN_INNER <= bits && bits <= Self::MAX_INNER {
400                        return Some(match_signedness! {
401                            match $signed_prefix {
402                                // the sign bit and friends may be on
403                                // mask it just in case
404                                SIGNED => { Self::new_truncated(bits) },
405                                // unsigned no need to mask the top bits
406                                // they are already off
407                                UNSIGNED => { unsafe { Self::from_bits(bits) } },
408                            }
409                        })
410                    }
411
412                    None
413                }
414
415                /// # Safety
416                #[doc = concat!(
417                    "the number must only have the first ",
418                    $byte_count,
419                    " with zeros, the upper bytes must NOT be filled"
420                )]
421                #[inline(always)]
422                pub const unsafe fn from_bits(bits: $backing_ty) -> Self {
423                    // Safety: upheld by user
424                    unsafe { core::mem::transmute(bits) }
425                }
426
427                /// truncates the upper bytes and sets them to zero
428                #[inline(always)]
429                pub const fn new_truncated(bits: $backing_ty) -> Self {
430                    unsafe {
431                        Self::from_bits(bits & Self::DATA_BITS_MASK)
432                    }
433                }
434
435                #[inline(always)]
436                pub const fn to_bits(self) -> $backing_ty {
437                    // Safety: self is always in the same layout as backing
438                    unsafe { core::mem::transmute(self) }
439                }
440
441                #[inline(always)]
442                pub const fn swapped_bytes(self) -> [u8; $byte_count] {
443                    let bits = self.to_bits();
444                    let swapped = bits.swap_bytes();
445
446                    // Safety:
447                    // swapped just swaps the bytes and so is a valid
448                    // flipped version of self which is what
449                    // [<Flipped $byte_count IntInner>] is
450                    let swapped: [<Flipped $signed_prefix $bit_count Inner>] = unsafe {
451                        core::mem::transmute(swapped)
452                    };
453                    
454                    swapped.bytes
455                }
456
457                #[inline(always)]
458                pub const fn swap_bytes(self) -> Self {
459                    // this has surprisingly very good codegen
460                    // and is as good if not better than anything manual after inlining
461                    Self([<$signed_prefix $bit_count Inner>] {
462                        zeros: [Zero::_0; { $size - $byte_count }],
463                        bytes: self.swapped_bytes()
464                    })
465                }
466
467                #[inline(always)]
468                pub const fn to_le(self) -> Self {
469                    #[cfg(target_endian = "little")]
470                    {
471                        self
472                    }
473
474                    #[cfg(target_endian = "big")]
475                    {
476                        self.swap_bytes()
477                    }
478                }
479
480                #[inline(always)]
481                pub const fn to_ne_bytes(self) -> [u8; $byte_count] {
482                    self.0.bytes
483                }
484
485                #[inline(always)]
486                pub const fn to_le_bytes(self) -> [u8; $byte_count] {
487                    #[cfg(target_endian = "little")]
488                    {
489                        self.0.bytes
490                    }
491
492                    #[cfg(target_endian = "big")]
493                    {
494                        self.swapped_bytes()
495                    }
496                }
497
498                #[inline(always)]
499                pub const fn to_be_bytes(self) -> [u8; $byte_count] {
500                    #[cfg(target_endian = "big")]
501                    {
502                        self.0.bytes
503                    }
504
505                    #[cfg(target_endian = "little")]
506                    {
507                        self.swapped_bytes()
508                    }
509                }
510
511                #[inline(always)]
512                pub const fn get(self) -> $backing_ty {
513                    match_signedness! {
514                        match $signed_prefix {
515                            SIGNED => {
516                                {
517                                    const OFFSET: u32 = $backing_ty::BITS - <[<$signed_prefix:lower $bit_count>]>::BITS;
518                                    // FIXME unchecked_shifts
519                                    // this aligns the integers sign bit
520                                    // to the sign bit of the backing type
521                                    // and then sign extends backwards
522                                    (self.to_bits() << OFFSET) >> OFFSET
523                                }
524                            },
525                            // if the data is not signed no special handling is required
526                            UNSIGNED => { self.to_bits() },
527                        }
528                    }
529                }
530            }
531
532            defer_impl!(@($signed_prefix) [<$signed_prefix:lower $bit_count>] as $backing_ty);
533        )*}
534
535        )*
536
537
538        pub mod const_macro {
539            pastey::paste! {$($(
540                #[macro_export]
541                macro_rules! [<$signed_prefix:lower $bit_count>] {
542                    ($__inner_expr__: expr) => {
543                        const {
544                            match $crate::[<$signed_prefix:lower $bit_count>]::new($__inner_expr__) {
545                                Some(x) => x,
546                                None => panic!(
547                                    concat!("invalid number constant")
548                                )
549                            }
550                        }
551                    }
552                }
553
554                pub use [<$signed_prefix:lower $bit_count>];
555            )*)*}
556        }
557    };
558}
559
560macro_rules! nbyte_int {
561    (
562        $($backing: tt @($size: expr) [$($byte_count: tt @ $bit_count: tt),+ $(,)?])*
563    ) => {
564        pastey::paste! {
565            nbyte_int_inner! {
566                $(
567                [<i $backing>] @(I $size) [$($byte_count @ $bit_count),+]
568                [<u $backing>] @(U $size) [$($byte_count @ $bit_count),+]
569                )*
570            }
571        }
572    };
573}
574
575nbyte_int! {
576    // 8 bits and 16 bits have no in between
577    32 @(4) [3 @ 24]
578    64 @(8) [5 @ 40, 6 @ 48, 7 @ 56]
579    128 @(16) [9 @ 72, 10 @ 80, 11 @ 88, 12 @ 96, 13 @ 104, 14 @ 112, 15 @ 120]
580}