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 #[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 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 #[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 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 #[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 let abs_value = (Self::DATA_BITS_MASK + 1) >> 1;
426 -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 #[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 SIGNED => { Self::new_truncated(bits) },
457 UNSIGNED => { unsafe { Self::from_bits(bits) } },
460 }
461 })
462 }
463
464 None
465 }
466
467 #[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 unsafe { core::mem::transmute(bits) }
477 }
478
479 #[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 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 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 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 (self.to_bits() << OFFSET) >> OFFSET
603 }
604 },
605 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 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}