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 #[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 #[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 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 #[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 let abs_value = (Self::DATA_BITS_MASK + 1) >> 1;
374 -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 #[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 SIGNED => { Self::new_truncated(bits) },
405 UNSIGNED => { unsafe { Self::from_bits(bits) } },
408 }
409 })
410 }
411
412 None
413 }
414
415 #[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 unsafe { core::mem::transmute(bits) }
425 }
426
427 #[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 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 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 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 (self.to_bits() << OFFSET) >> OFFSET
523 }
524 },
525 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 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}