Skip to main content

intid_core/
uint.rs

1//! Defines the [`UnsignedPrimInt`] trait and its generic operations.
2//!
3//! These are module functions rather than trait functions
4//! to avoid polluting the primitive integer namespaces.
5
6use core::fmt::{Debug, Display, Formatter};
7use core::hash::Hash;
8
9mod sealed;
10
11maybe_trait_bound!(
12    MaybeNumTrait,
13    cfg(feature = "num-traits"),
14    num_traits::PrimInt
15);
16maybe_trait_bound!(MaybePod, cfg(feature = "bytemuck"), bytemuck::Pod);
17maybe_trait_bound!(
18    MaybeContiguous,
19    cfg(feature = "bytemuck"),
20    bytemuck::Contiguous
21);
22#[doc(hidden)]
23pub trait ConvertPrimInts:
24    From<u8>
25    + TryFrom<u8>
26    + TryFrom<u16>
27    + TryFrom<u32>
28    + TryFrom<u64>
29    + TryFrom<u128>
30    + TryFrom<usize>
31    + TryInto<u8>
32    + TryInto<u16>
33    + TryFrom<u32>
34    + TryFrom<u64>
35    + TryInto<u128>
36    + TryInto<usize>
37{
38}
39
40/// An unsigned primitive integer.
41///
42/// Most methods in this trait are only available through the [`intid::uint`](crate::uint) module
43/// in order to avoid conflict with inherit implementations and other traits.
44/// You can get access to more functionality by enabling the `num-traits` or `bytemuck` features,
45/// which will add [`num_traits::PrimInt`] and [`bytemuck::Pod`] bounds respectively.
46pub trait UnsignedPrimInt:
47    Eq
48    + Hash
49    + Ord
50    + Copy
51    + Default
52    + Debug
53    + Display
54    + ConvertPrimInts
55    + sealed::PrivateUnsignedInt
56    + MaybeNumTrait
57    + MaybePod
58    + MaybeContiguous
59{
60}
61
62/// Cast from one [`UnsignedPrimInt`] into another,
63/// returning `None` if there is overflow.
64#[inline]
65pub fn checked_cast<T: UnsignedPrimInt, U: UnsignedPrimInt>(value: T) -> Option<U> {
66    sealed::PrivateUnsignedInt::checked_cast(value)
67}
68
69/// Add the specified value to the integer,
70/// returning `None` if overflow occurs.
71#[inline]
72pub fn checked_add<T: UnsignedPrimInt>(left: T, right: T) -> Option<T> {
73    sealed::PrivateUnsignedInt::checked_add(left, right)
74}
75
76/// Subtract the specified value from the integer,
77/// returning `None` if overflow occurs.
78#[inline]
79pub fn checked_sub<T: UnsignedPrimInt>(left: T, right: T) -> Option<T> {
80    sealed::PrivateUnsignedInt::checked_sub(left, right)
81}
82
83/// Convert a primitive integer to a [`usize`],
84/// returning `None` if overflow occurs.
85#[inline]
86pub fn to_usize_checked<T: UnsignedPrimInt>(val: T) -> Option<usize> {
87    T::to_usize_checked(val)
88}
89
90/// Convert a primitive integer to a [`usize`],
91/// wrapping around on overflow.
92#[inline]
93pub fn to_usize_wrapping<T: UnsignedPrimInt>(val: T) -> usize {
94    T::to_usize_wrapping(val)
95}
96
97/// Convert a primitive integer to a [`usize`],
98/// returning `None` if overflow occurs.
99#[inline]
100pub fn from_usize_checked<T: UnsignedPrimInt>(val: usize) -> Option<T> {
101    T::from_usize_checked(val)
102}
103
104/// Convert a primitive integer to a [`usize`],
105/// wrapping around if overflow occurs.
106#[inline]
107pub fn from_usize_wrapping<T: UnsignedPrimInt>(val: usize) -> T {
108    T::from_usize_wrapping(val)
109}
110
111/// Determine the zero value of the specified `UnsignedPrimInt`.
112///
113/// This function always succeeds (a `NonZero` is not a primitive integer)
114#[inline]
115pub const fn zero<T: UnsignedPrimInt>() -> T {
116    T::ZERO
117}
118
119/// Determine the one value of the specified `UnsignedPrimInt`.
120#[inline]
121pub const fn one<T: UnsignedPrimInt>() -> T {
122    T::ONE
123}
124
125/// Determine the maximum value of the specified [`UnsignedPrimInt`].
126#[inline]
127pub const fn max_value<T: UnsignedPrimInt>() -> T {
128    T::MAX
129}
130
131/// Determine the number of bits needed to represent the specified [`UnsignedPrimInt`].
132#[inline]
133pub const fn bits<T: UnsignedPrimInt>() -> u32 {
134    T::BITS
135}
136
137/// Get the number of trailing zeroes for the specified integer.
138///
139/// See [`u64::trailing_zeros`] for details.
140#[inline]
141pub fn trailing_zeros<T: UnsignedPrimInt>(val: T) -> u32 {
142    sealed::PrivateUnsignedInt::trailing_zeros(val)
143}
144
145/// Get the number of leading zeroes for the specified integer.
146///
147/// See [`u64::leading_zeros`] for details.
148#[inline]
149pub fn leading_zeros<T: UnsignedPrimInt>(val: T) -> u32 {
150    sealed::PrivateUnsignedInt::leading_zeros(val)
151}
152
153/// Count the number of one bits in the specified integer.
154///
155/// See [`u64::count_ones`] for details.
156#[inline]
157pub fn count_ones<T: UnsignedPrimInt>(val: T) -> u32 {
158    sealed::PrivateUnsignedInt::count_ones(val)
159}
160
161/// Attempt to describe the specified [`UnsignedPrimInt`]
162/// in a format suitable for debugging or panic messages.
163///
164/// This differs from the standard `Display` and `Debug` implementation,
165/// because `T::MAX` is special-cased.
166///
167/// *WARNING*: This representation may change without warning in the future,
168/// so the exact representation should not be relied upon.
169///
170/// ## Examples
171/// ```
172/// use intid_core::uint::debug_desc;
173/// assert_eq!(
174///     debug_desc(3u32).to_string(),
175///     "3"
176/// );
177/// assert_eq!(
178///     debug_desc(u32::MAX).to_string(),
179///     "u32::MAX"
180/// )
181/// ```
182#[cold]
183pub fn debug_desc<T: UnsignedPrimInt>(value: T) -> DebugDesc<T> {
184    DebugDesc(value)
185}
186
187/// The description of an unsigned integer returned by [`debug_desc`].
188#[derive(Clone)]
189pub struct DebugDesc<T: UnsignedPrimInt>(T);
190impl<T: UnsignedPrimInt> Display for DebugDesc<T> {
191    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
192        if self.0 == T::MAX {
193            f.write_str(T::TYPE_NAME)?;
194            f.write_str("::MAX")
195        } else {
196            <T as Display>::fmt(&self.0, f)
197        }
198    }
199}
200impl<T: UnsignedPrimInt> Debug for DebugDesc<T> {
201    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
202        <Self as Display>::fmt(self, f)
203    }
204}
205
206/// Panic with a message indicating that an ID is not valid.
207///
208/// Used to implement the panic in [`crate::IntegerId::from_int`].
209#[inline(never)]
210#[track_caller]
211#[cold]
212pub(crate) fn invalid_id<T: UnsignedPrimInt>(id: T) -> ! {
213    panic!("Invalid id: {}", debug_desc(id))
214}