intid_allocator/unique.rs
1use crate::IdExhaustedError;
2use core::cell::Cell;
3use intid::{uint, IntegerIdCounter};
4
5#[cfg(feature = "atomic")]
6pub mod atomic;
7
8/// Allocates unique integer ids.
9///
10/// Guarantees that each call to the [`Self::alloc`] function will return a unique id,
11/// unless [`Self::reset`] is called.
12///
13/// Ids start at [`IntegerIdCounter::START`] by default, counting upwards from there.
14#[derive(Clone, Debug)]
15pub struct UniqueIdAllocator<T: IntegerIdCounter> {
16 next_id: Cell<Option<T>>,
17}
18impl<T: IntegerIdCounter> Default for UniqueIdAllocator<T> {
19 fn default() -> Self {
20 Self::new()
21 }
22}
23impl<T: IntegerIdCounter> UniqueIdAllocator<T> {
24 /// Return the maximum currently used id,
25 /// or `None` if no ids have been allocated yet.
26 #[inline]
27 pub fn max_used_id(&self) -> Option<T> {
28 self.next_id
29 .get()
30 .and_then(|id| IntegerIdCounter::checked_sub(id, uint::one()))
31 }
32
33 /// Create a new allocator,
34 /// using [`T::START`] as the first id (usually zero).
35 ///
36 /// [`T::START`]: IntegerIdCounter::START
37 #[inline]
38 pub const fn new() -> Self {
39 Self::with_start(T::START)
40 }
41
42 /// Create a new allocator,
43 /// using the specified value as the first id.
44 ///
45 /// Equivalent to calling [`Self::new`] then [`Self::set_next_id`] in sequence.
46 #[inline]
47 pub const fn with_start(start: T) -> Self {
48 UniqueIdAllocator {
49 next_id: Cell::new(Some(start)),
50 }
51 }
52
53 /// Attempt to allocate a new id,
54 /// panicking if none are available.
55 ///
56 /// # Panics
57 /// This will panic when the range of the underlying [`IntegerIdCounter`] is exhausted.
58 /// See [`Self::try_alloc`] for a version that returns an error instead.
59 #[inline]
60 #[track_caller]
61 pub fn alloc(&self) -> T {
62 match self.try_alloc() {
63 Ok(id) => id,
64 Err(e) => e.panic(),
65 }
66 }
67
68 /// Attempt to allocate a new id,
69 /// returning an error if there are no more available.
70 ///
71 /// # Errors
72 /// If the range of the underlying [`IntegerIdCounter`] is exhausted,
73 /// this will return an error.
74 #[inline]
75 pub fn try_alloc(&self) -> Result<T, IdExhaustedError<T>> {
76 let old_id = self.next_id.get().ok_or_else(IdExhaustedError::new)?;
77 self.next_id
78 .set(IntegerIdCounter::checked_add(old_id, intid::uint::one()));
79 Ok(old_id)
80 }
81
82 /// Set the id that will be returned from the [`Self::alloc`] function.
83 ///
84 /// Like a call to [`Self::reset`], this may cause the counter to unexpectedly jump backwards.
85 /// It may also cause the counter to jump unexpectedly forwards.
86 /// Keep the allocator private if this behavior is undesired.
87 #[inline]
88 pub fn set_next_id(&self, next_id: T) {
89 self.next_id.set(Some(next_id));
90 }
91
92 /// Reset the allocator to a pristine state,
93 /// beginning allocations all over again.
94 ///
95 /// This is equivalent to running `*allocator = UniqueIdAllocator::new()`,
96 /// but does not require a `&mut Self` reference.
97 ///
98 /// This may cause unexpected behavior if ids are expected to be monotonically increasing,
99 /// or if the new ids conflict with ones still in use.
100 /// To avoid this, keep the id allocator private.
101 ///
102 /// See also the [`Self::set_next_id`] function,
103 /// which can cause the counter to jump forwards in addition to jumping backwards.
104 #[inline]
105 pub fn reset(&self) {
106 self.set_next_id(T::START);
107 }
108}