use crate::IdExhaustedError;
#[allow(unused_imports)] use crate::{IntegerId, UniqueIdAllocator};
use core::marker::PhantomData;
use core::sync::atomic::Ordering;
use intid::{uint, IntegerIdCounter};
#[derive(Debug)]
pub struct UniqueIdAllocatorAtomic<T: IntegerIdCounter> {
next_id: atomic::Atomic<T::Int>,
marker: PhantomData<T>,
}
impl<T: IntegerIdCounter> Default for UniqueIdAllocatorAtomic<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: IntegerIdCounter> UniqueIdAllocatorAtomic<T> {
#[inline]
pub const fn new() -> Self {
UniqueIdAllocatorAtomic {
next_id: atomic::Atomic::new(T::START_INT),
marker: PhantomData,
}
}
#[inline]
pub fn with_start(start: T) -> Self {
UniqueIdAllocatorAtomic {
next_id: atomic::Atomic::new(start.to_int()),
marker: PhantomData,
}
}
#[track_caller]
pub const fn with_start_const(start: T) -> Self
where
T: bytemuck::NoUninit,
{
let start = bytemuck::must_cast::<T, T::Int>(start);
UniqueIdAllocatorAtomic {
next_id: atomic::Atomic::new(start),
marker: PhantomData,
}
}
#[inline]
pub fn approx_max_used_id(&self) -> Option<T> {
IntegerIdCounter::checked_sub(
T::from_int_checked(self.next_id.load(Ordering::Relaxed))?,
uint::one(),
)
}
#[inline]
pub fn try_alloc(&self) -> Result<T, IdExhaustedError<T>> {
self.next_id
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |x| {
uint::checked_add(x, uint::one())
})
.ok()
.and_then(T::from_int_checked)
.ok_or_else(IdExhaustedError::new)
}
#[inline]
#[must_use]
pub fn alloc(&self) -> T {
match self.try_alloc() {
Ok(x) => x,
Err(e) => e.panic(),
}
}
#[inline]
pub fn reset(&self) {
self.next_id.store(T::START.to_int(), Ordering::Relaxed);
}
}