Skip to main content

intid_core/
lib.rs

1//! Defines the [`IntegerId`] trait, for types that can be identified by an integer value.
2//!
3//! This contains all the same types that the [`intid`] crate does,
4//! but has no dependency on [`intid_derive`] (even when the `intid/derive` feature is enabled).
5//! This reduces compile times, similar to the separation between `serde_core` and `serde` introduced in [serde-rs/serde#2608].
6//!
7//! It may be convenient to rename the `intid_core` dependency to `intid` using [dependency renaming].
8//! ```toml
9//! intid = { version = "0.3", package = "intid_core" }
10//! ```
11//! This renaming comes at no loss of clarity,
12//! since the items in `intid_core` are simply a subset of the items in the `intid` crate.
13//! If for some reason you decide to use `intid_derive` directly without depending on `intid`,
14//! then you will need to do this renaming since the derived code references the `intid` crate.
15//!
16//! [dependency renaming]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#renaming-dependencies-in-cargotoml
17//! [serde-rs/serde#2608]: https://github.com/serde-rs/serde/pull/2608
18//! [`intid`]: https://docs.rs/intid/latest/intid
19//! [`intid_derive`]: https://docs.rs/intid-derive/latest/intid_derive
20#![no_std]
21#![cfg_attr(feature = "nightly", feature(never_type,))]
22extern crate alloc;
23
24/// The `primint` crate used to abstract over primitive integer types.
25///
26/// The central traits used are [`primint::PrimitiveInt`] and [`primint::UnsignedPrimInt`].
27/// All bounds in this crate use [`primint::UnsignedPrimInt`],
28/// as signed integers are not useful for our purposes.
29pub extern crate primint;
30
31use core::fmt::Debug;
32
33#[macro_use]
34mod macros;
35#[doc(hidden)]
36pub mod array;
37mod impls;
38pub mod trusted;
39pub mod uint;
40pub mod utils;
41
42pub use primint::UnsignedPrimInt;
43
44/// An identifier which can be sensibly converted to/from an unsigned integer value.
45///
46///
47/// The type should not carry any information beyond that of the integer index,
48/// and be able to losslessly convert back and forth from [`Self::Int`].
49/// It is possible that not all values of the underlying integer type are valid,
50/// allowing [`core::num::NonZero`] and C-like enums to implement this trait.
51///
52///
53/// This is intended mostly for newtype wrappers around integer indexes,
54/// and the primitive integer types themselves.
55///
56/// The value of the underlying integer must be consistent.
57/// It cannot change over the course of the program's lifetime.
58///
59/// ## Safety
60/// With one exception, this trait is safe to implement and cannot be relied upon by memory safety.
61///
62/// If the implementation of [`IntegerId::from_int_unchecked`] makes any sort of unsafe assumptions
63/// about the validity of the input, then the rest of the trait must be implemented correctly.
64/// This means that implementations of this trait fall into two categories:
65/// 1. Potentially incorrect implemented entirely using safe code, where `from_int_unchecked(x)`
66///    is equivalent to calling `from_int_checked(x).unwrap()`;
67/// 2. Traits where `from_int_unchecked` could trigger undefined behavior on an invalid value,
68///    but every other part of this trait can be trusted to be implemented correctly.
69///
70/// In both these cases, the following code is always safe:
71/// ```no_run
72/// # use intid_core::IntegerId;
73/// fn foo<T: IntegerId>(x: T) -> T {
74///     let y = x.to_int();
75///     let z = unsafe { T::from_int_unchecked(y) };
76///     z
77/// }
78/// ```
79/// In case 1,  it doesn't matter if [`x.to_int()`](Self::to_int) produces garbage data,
80/// because `T::from_int_unchecked` method is safe to call.
81/// In case 2, the `to_int` method can be trusted to produce a valid value `y` that cannot fail
82/// when passed to `T::from_int_unchecked`.
83///
84/// The requirement for correctness in this case also apply to all sub-traits in this crate,
85/// including [`IntegerIdContiguous`] and [`IntegerIdCounter`].
86/// So an unsafe implementation of `from_int_unchecked` can be similarly trusted to accept
87/// all integer values between [`IntegerId::MIN_ID`] and [`IntegerId::MAX_ID`].
88///
89/// This restriction allows avoiding unnecessary checks when ids are stored to/from another data structure.
90/// Despite this requirement, I still consider this trait safe to implement,
91/// because safety can only be violated by an unsafe implementation of`from_int_unchecked`.
92///
93/// This type should not have interior mutability.
94/// This is guaranteed by the `Copy` bound.
95pub trait IntegerId: Copy + Eq + Debug + Send + Sync + 'static {
96    /// The underlying integer type.
97    ///
98    /// Every valid instance of `Self` should correspond to a valid `Self::Int`.
99    /// However, the other direction may not always be true.
100    type Int: primint::UnsignedPrimInt;
101    /// The value of this type with the smallest integer value,
102    /// or `None` if this type is uninhabited.
103    const MIN_ID: Option<Self>;
104    /// The value of this type with the largest integer value,
105    /// or `None` if this type is uninhabited.
106    const MAX_ID: Option<Self>;
107    /// The value of [`Self::MIN_ID`] a primitive integer,
108    /// or `None` if this type is uninhabited.
109    ///
110    /// This is necessary because trait methods cannot be marked `const`.
111    const MIN_ID_INT: Option<Self::Int>;
112    /// The value of [`Self::MAX_ID`] a primitive integer,
113    /// or `None` if this type is uninhabited.
114    ///
115    /// This is necessary because trait methods cannot be marked `const`.
116    const MAX_ID_INT: Option<Self::Int>;
117
118    /// Indicates that the type's implementation of [`IntegerId::to_int`] can be trusted
119    /// to only return values in the range `MIN_ID_INT..=MAX_ID_INT`.
120    ///
121    /// Creating this token means that all of these guarantees can be relied upon for memory safety.
122    /// This allows unsafe code to avoid bounds checks,
123    /// but turns a correctness invariant into a soundness invariant.
124    ///
125    /// # Safety
126    /// The result of [`Self::to_int`] must always fall in the range `MIN_ID_INT..=MAX_ID_INT`.
127    ///
128    /// If [`EnumId`] is implemented,
129    /// then the requirements of the [`EnumId`] trait must be met as well.
130    /// In particular, the index must always fit in a `u32`
131    /// and have the appropriately `Array` and `BitSet` items.
132    const TRUSTED_RANGE: Option<trusted::TrustedRangeToken<Self>> = None;
133
134    /// Create an id from the underlying integer value,
135    /// panicking if the value is invalid.
136    ///
137    /// ## Correctness
138    /// A value returned by this method should never trigger
139    /// an error if passed to [`Self::from_int_checked`].
140    /// This means the validity of certain ids can't change over the course of the program.
141    #[inline]
142    #[track_caller]
143    fn from_int(id: Self::Int) -> Self {
144        match Self::from_int_checked(id) {
145            Some(success) => success,
146            None => uint::invalid_id(id),
147        }
148    }
149
150    /// Create an id from the underlying integer value,
151    /// returning `None` if the value is invalid.
152    fn from_int_checked(id: Self::Int) -> Option<Self>;
153
154    /// Create an id from the underlying integer value,
155    /// triggering undefined behavior if the value is invalid.
156    ///
157    /// ## Safety
158    /// If the corresponding [`Self::from_int_checked`] method would fail,
159    /// this triggers undefined behavior.
160    /// The default implementation just invokes [`Self::from_int`].
161    #[inline]
162    unsafe fn from_int_unchecked(id: Self::Int) -> Self {
163        Self::from_int(id)
164    }
165
166    /// Convert this id into an underlying integer type.
167    ///
168    /// This method can never fail,
169    /// since valid instances `Self` always correspond to valid instances of `Self::Int`.
170    fn to_int(self) -> Self::Int;
171}
172
173/// Indicates that an id occupies contiguous range of contiguous values,
174/// and all values between [`IntegerId::MIN_ID`] and [`IntegerId::MAX_ID`] are valid.
175///
176/// This is similar to [`bytemuck::Contiguous`].
177/// However, since it is safe to implement,
178/// it must not be relied upon for correctness.
179///
180/// ## Safety
181/// This trait is safe to implement, so may not usually be relied upon for memory safety.
182///
183/// However, if [`Self::from_int_unchecked`](IntegerId::from_int_unchecked) makes unsafe assumptions (satisfying the condition set forth in the [`IntegerId`] safety docs),
184/// then this trait must also be implemented correctly.
185/// More specifically, all integers between [`IntegerId::MIN_ID`] and [`IntegerId::MAX_ID`] must be valid
186/// and cannot fail when passed to [`IntegerId::from_int_checked`].
187pub trait IntegerIdContiguous: IntegerId {}
188
189/// An [`IntegerId`] that can be sensibly used as a counter,
190/// starting at a [`Self::START`] value and being incremented from there.
191///
192/// This is used by the `intid-allocator` crate to provide an atomic counter to allocate new ids.
193/// It also provides more complex allocators that can reuse ids that have been freed.
194///
195/// This type cannot be implemented for uninhabited types like [`core::convert::Infallible`] or `!`,
196/// as there is no valid implementation of [`Self::START`].
197pub trait IntegerIdCounter: IntegerId + IntegerIdContiguous {
198    /// Where a counter a should start from.
199    ///
200    /// This should be the [`Default`] value if one is defined.
201    /// It is usually equal to the [`IntegerId::MIN_ID`],
202    /// but this is not required.
203    const START: Self;
204    /// The value of [`Self::START`] as a [`T::Int`](IntegerId::Int).
205    ///
206    /// This is necessary because trait methods ([`IntegerId::to_int`])
207    /// can not currently be const methods.
208    const START_INT: Self::Int;
209
210    /// Increment this value by the specified offset,
211    /// returning `None` if the value overflows or is invalid.
212    ///
213    /// This should behave consistently with [`IntegerIdContiguous`]
214    /// and [`IntegerId::from_int_checked`].
215    /// However, that can not be relied upon for memory safety.
216    ///
217    /// This is implemented as an associated method to avoid namespace pollution.
218    #[inline]
219    fn checked_add(this: Self, offset: Self::Int) -> Option<Self> {
220        primint::checked_add(this.to_int(), offset).and_then(Self::from_int_checked)
221    }
222
223    /// Increment this value by the specified offset,
224    /// returning `None` if the value overflows or is invalid.
225    ///
226    /// This should behave consistently with [`IntegerIdContiguous`]
227    /// and [`IntegerId::from_int_checked`].
228    /// However, that can not be relied upon for memory safety.
229    ///
230    /// This is implemented as an associated method to avoid namespace pollution.
231    #[inline]
232    fn checked_sub(this: Self, offset: Self::Int) -> Option<Self> {
233        primint::checked_sub(this.to_int(), offset).and_then(Self::from_int_checked)
234    }
235}
236
237/// An [`IntegerId`] which are limited to small set of indexes.
238///
239/// As the name suggests, it is most useful for C-style enums,
240/// allowing use with [`EnumMap`] and [`EnumSet`] collections which do not do any allocation.
241/// Is not implemented for types like `u32` where inline storage
242/// would require inordinate amounts of space (an `EnumMap<u32, u8>`would take 4GiB).
243///
244/// For this reason, all valid indexes and the value [`Self::MAX_ID_INT + 1`](IntegerId::MAX_ID_INT)
245/// must fit into both a [`u32`] and a [`usize`].
246/// This requirement prevents a `u32` from implementing `EnumId`,
247/// as `u32::MAX + 1` exceeds 32-bits. Implementing `EnumId for u32` would also be invalid on
248/// 16-bit architectures, as then a `u32` index would not fit into a `usize`.
249///
250/// Note that this trait does not imply [`IntegerIdContiguous`].
251/// This means that while [`x <= Self::MAX_ID_INT`](IntegerId::MAX_ID_INT) is a necessary condition
252/// for [`Self::from_int(x)`](IntegerId::from_int) to succeed, it is not always a sufficient condition.
253///
254/// [`EnumMap`]: https://docs.rs/idmap/latest/idmap/enums/map/struct.EnumMap.html
255/// [`EnumSet`]: https://docs.rs/idmap/latest/idmap/enums/set/struct.EnumSet.html
256pub trait EnumId: IntegerId {
257    /// The total number of valid values.
258    ///
259    /// This value is at most equal to [`Self::MAX_ID_INT`](IntegerId::MAX_ID_INT),
260    /// which must fit in a [`u32`] due to the trait requirements.
261    const COUNT: u32;
262    /// A builtin array of `[T; {Self::MAX_ID_INT + 1}]`.
263    ///
264    /// Necessary to work around the current (Rust 1.90) restrictions on const generics
265    ///
266    /// # Safety
267    /// Since the [`array::Array`] trait is sealed,
268    /// this is guaranteed to be a builtin array of type `T`.
269    /// Since this is a safe trait, the length could be any value.
270    /// However, that is easily checked using a const assertion.
271    type Array<T>: array::Array<T>;
272    /// An array of words, whose bits can store all valid ids.
273    ///
274    /// Necessary to work around the current (Rust 1.90) restrictions on const generics.
275    ///
276    /// # Safety
277    /// Has similar safety guarantees as [`Self::Array`].
278    /// The type is correct, but the length must be checked with a const assertion.
279    type BitSet: array::Array<array::BitsetLimb>;
280}
281impl EnumId for u8 {
282    const COUNT: u32 = {
283        assert!(u8::MAX as u32 + 1 == 256);
284        256
285    };
286    type Array<T> = [T; 256];
287    type BitSet = [u64; 4];
288}
289
290/// A type that can be for lookup as an [`IntegerId`].
291///
292/// Used for key lookup in maps, similar to [`core::borrow::Borrow`] or [`equivalent::Equivalent`].
293/// These traits are not suitable for id maps,
294/// which need conversion to integers rather than hashing/equality.
295///
296/// [`equivalent::Equivalent`]: https://docs.rs/equivalent/latest/equivalent/trait.Equivalent.html
297pub trait EquivalentId<K: IntegerId> {
298    /// Convert this type to an id `K`.
299    fn as_id(&self) -> K;
300}
301impl<K: IntegerId> EquivalentId<K> for K {
302    #[inline]
303    fn as_id(&self) -> K {
304        *self
305    }
306}
307impl<K: IntegerId> EquivalentId<K> for &'_ K {
308    #[inline]
309    fn as_id(&self) -> K {
310        **self
311    }
312}
313impl<K: IntegerId> EquivalentId<K> for &'_ mut K {
314    #[inline]
315    fn as_id(&self) -> K {
316        **self
317    }
318}