bounded_integer/
prim_int.rs

1#![expect(clippy::must_use_candidate)]
2
3use core::fmt::{self, Display, Formatter};
4use core::num::NonZero;
5
6/// An error returned when a checked conversion into a bounded integer fails.
7#[derive(Debug, Clone, Copy)]
8#[non_exhaustive]
9pub struct TryFromError;
10
11impl Display for TryFromError {
12    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
13        f.write_str("out of range conversion to bounded integer attempted")
14    }
15}
16
17#[cfg(feature = "std")]
18impl std::error::Error for TryFromError {}
19
20#[must_use]
21pub fn try_from_error() -> TryFromError {
22    TryFromError
23}
24
25pub trait PrimInt {
26    type Signed;
27    type Unsigned;
28}
29
30pub type Signed<T> = <T as PrimInt>::Signed;
31pub type Unsigned<T> = <T as PrimInt>::Unsigned;
32
33generate! {
34    u8 i8,
35    u16 i16,
36    u32 i32,
37    u64 i64,
38    u128 i128,
39    usize isize,
40}
41
42macro_rules! generate {
43    ($($unsigned:ident $signed:ident,)*) => { $(
44        impl PrimInt for $unsigned {
45            type Signed = $signed;
46            type Unsigned = $unsigned;
47        }
48        impl PrimInt for $signed {
49            type Signed = $signed;
50            type Unsigned = $unsigned;
51        }
52
53        impl crate::__private::Dispatch<$unsigned> {
54            pub const fn checked_add_unsigned(lhs: $unsigned, rhs: $unsigned) -> Option<$unsigned> {
55                lhs.checked_add(rhs)
56            }
57            pub const fn checked_sub_unsigned(lhs: $unsigned, rhs: $unsigned) -> Option<$unsigned> {
58                lhs.checked_sub(rhs)
59            }
60            pub const fn rem_euclid_unsigned(lhs: $unsigned, rhs: NonZero<$unsigned>) -> $unsigned {
61                lhs.rem_euclid(rhs.get())
62            }
63        }
64        impl crate::__private::Dispatch<$signed> {
65            pub const fn checked_add_unsigned(lhs: $signed, rhs: $unsigned) -> Option<$signed> {
66                lhs.checked_add_unsigned(rhs)
67            }
68            pub const fn checked_sub_unsigned(lhs: $signed, rhs: $unsigned) -> Option<$signed> {
69                lhs.checked_sub_unsigned(rhs)
70            }
71            pub const fn rem_euclid_unsigned(lhs: $signed, rhs: NonZero<$unsigned>) -> $unsigned {
72                // In my benchmarks, this is faster than methods involving widening.
73                #[expect(clippy::cast_possible_wrap)]
74                #[expect(clippy::cast_sign_loss)]
75                if 0 <= lhs {
76                    // If `lhs` is nonnegative, just use regular unsigned remainder.
77                    (lhs as $unsigned).rem_euclid(rhs.get())
78                } else if 0 <= rhs.get() as $signed {
79                    // If `rhs` is small enough to fit in a signed type, use regular `rem_euclid`.
80                    // We know the result is nonnegative, so we can cast to the unsigned type.
81                    lhs.rem_euclid(rhs.get() as $signed) as $unsigned
82                } else {
83                    // Otherwise, `lhs` is negative and `rhs` is larger than all signed values. We
84                    // can therefore add `rhs` to `lhs` and get an unsigned value without overflow,
85                    // which won’t affect the result.
86                    rhs.get().checked_add_signed(lhs).unwrap().rem_euclid(rhs.get())
87                }
88            }
89        }
90    )* };
91}
92use generate;
93
94// We don’t implement for `usize` because we don’t get `(wide usize): From<usize>` impls which
95// makes things difficult trait-wise.
96pub trait HasWide {
97    type Wide;
98}
99
100pub type Wide<T> = <T as HasWide>::Wide;
101
102impl HasWide for u8 {
103    type Wide = u16;
104}
105impl HasWide for u16 {
106    type Wide = u32;
107}
108impl HasWide for u32 {
109    type Wide = u64;
110}
111impl HasWide for u64 {
112    type Wide = u128;
113}
114impl HasWide for i8 {
115    type Wide = i16;
116}
117impl HasWide for i16 {
118    type Wide = i32;
119}
120impl HasWide for i32 {
121    type Wide = i64;
122}
123impl HasWide for i64 {
124    type Wide = i128;
125}