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    #[inline]
45    pub const fn with_start(start: T) -> Self {
46        UniqueIdAllocator {
47            next_id: Cell::new(Some(start)),
48        }
49    }
50
51    /// Attempt to allocate a new id,
52    /// panicking if none are available.
53    ///
54    /// See [`Self::try_alloc`] for a version that returns an error
55    #[inline]
56    #[track_caller]
57    pub fn alloc(&self) -> T {
58        match self.try_alloc() {
59            Ok(id) => id,
60            Err(e) => e.panic(),
61        }
62    }
63
64    /// Attempt to allocate a new id,
65    /// returning an error if there are no more available.
66    #[inline]
67    pub fn try_alloc(&self) -> Result<T, IdExhaustedError<T>> {
68        let old_id = self.next_id.get().ok_or_else(IdExhaustedError::new)?;
69        self.next_id
70            .set(IntegerIdCounter::checked_add(old_id, intid::uint::one()));
71        Ok(old_id)
72    }
73
74    /// Reset the allocator to a pristine state,
75    /// beginning allocations all over again.
76    #[inline]
77    pub fn reset(&mut self) {
78        self.next_id.set(Some(T::START))
79    }
80}