use crate::Handle;
use core::num::NonZeroUsize;
use core::sync::atomic::AtomicUsize;
static NEXT_REGION_ID: AtomicUsize = AtomicUsize::new(1);
const SLOTMAP_MAX_RESERVE: usize = ((1u64 << 32) - 3) as usize;
const SLOTMAP_MAX_LIVE: usize = ((1u64 << 32) - 2) as usize;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RegionIdExhaustedError;
impl core::fmt::Display for RegionIdExhaustedError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("process-wide region_id counter exhausted")
}
}
impl core::error::Error for RegionIdExhaustedError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TryReserveError {
CapacityExceeded {
requested: usize,
limit: usize,
},
Overflow,
RegionIdExhausted(RegionIdExhaustedError),
}
impl core::fmt::Display for TryReserveError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::CapacityExceeded { requested, limit } => {
write!(f, "capacity {} exceeds slotmap limit {}", requested, limit)
}
Self::Overflow => f.write_str("capacity overflow"),
Self::RegionIdExhausted(inner) => inner.fmt(f),
}
}
}
impl core::error::Error for TryReserveError {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self {
Self::RegionIdExhausted(inner) => Some(inner),
_ => None,
}
}
}
impl From<RegionIdExhaustedError> for TryReserveError {
fn from(err: RegionIdExhaustedError) -> Self {
Self::RegionIdExhausted(err)
}
}
#[inline]
fn try_mint_region_id(counter: &AtomicUsize) -> Result<NonZeroUsize, RegionIdExhaustedError> {
use core::sync::atomic::Ordering;
match counter.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |current| {
if current == 0 {
None
} else if current == usize::MAX {
Some(0)
} else {
Some(current + 1)
}
}) {
Ok(0) => Err(RegionIdExhaustedError),
Ok(value) => match NonZeroUsize::new(value) {
Some(nz) => Ok(nz),
None => Err(RegionIdExhaustedError),
},
Err(_) => Err(RegionIdExhaustedError),
}
}
#[doc(hidden)]
pub fn dbg_try_mint_region_id(
counter: &AtomicUsize,
) -> Result<NonZeroUsize, RegionIdExhaustedError> {
try_mint_region_id(counter)
}
#[doc = include_str!("invariants.md")]
pub struct Region<T> {
region_id: NonZeroUsize,
inner: slotmap::SlotMap<slotmap::DefaultKey, T>,
}
impl<T> Region<T> {
#[inline]
fn owned_key(&self, handle: Handle<T>) -> Option<slotmap::DefaultKey> {
(handle.region_id == self.region_id).then_some(handle.key)
}
pub fn try_new() -> Result<Self, TryReserveError> {
let region_id = try_mint_region_id(&NEXT_REGION_ID)?;
Ok(Self {
region_id,
inner: slotmap::SlotMap::new(),
})
}
#[must_use]
pub fn new() -> Self {
Self::try_new().unwrap_or_else(|e| panic!("Region::new: {e}"))
}
pub fn try_with_capacity(capacity: usize) -> Result<Self, TryReserveError> {
if capacity > SLOTMAP_MAX_RESERVE {
return Err(TryReserveError::CapacityExceeded {
requested: capacity,
limit: SLOTMAP_MAX_RESERVE,
});
}
debug_assert!(capacity.checked_add(1).is_some());
let region_id = try_mint_region_id(&NEXT_REGION_ID)?;
Ok(Self {
region_id,
inner: slotmap::SlotMap::with_capacity(capacity),
})
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self::try_with_capacity(capacity).unwrap_or_else(|e| panic!("Region::with_capacity: {e}"))
}
#[must_use]
pub fn len(&self) -> usize {
self.inner.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
#[must_use]
pub fn capacity(&self) -> usize {
self.inner.capacity()
}
pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
let target = self
.inner
.len()
.checked_add(additional)
.ok_or(TryReserveError::Overflow)?;
if target > SLOTMAP_MAX_LIVE {
return Err(TryReserveError::CapacityExceeded {
requested: target,
limit: SLOTMAP_MAX_LIVE,
});
}
self.inner.reserve(additional);
Ok(())
}
pub fn reserve(&mut self, additional: usize) {
if let Err(e) = self.try_reserve(additional) {
panic!("Region::reserve: {e}");
}
}
#[must_use]
pub fn insert(&mut self, value: T) -> Handle<T> {
Handle::from_key_and_region(self.region_id, self.inner.insert(value))
}
#[must_use]
pub fn get(&self, handle: Handle<T>) -> Option<&T> {
self.inner.get(self.owned_key(handle)?)
}
#[must_use]
pub fn get_mut(&mut self, handle: Handle<T>) -> Option<&mut T> {
self.inner.get_mut(self.owned_key(handle)?)
}
#[must_use]
pub fn contains(&self, handle: Handle<T>) -> bool {
self.owned_key(handle)
.map(|key| self.inner.contains_key(key))
.unwrap_or(false)
}
pub fn remove(&mut self, handle: Handle<T>) -> Option<T> {
self.inner.remove(self.owned_key(handle)?)
}
#[must_use]
pub fn iter(&self) -> Iter<'_, T> {
Iter {
inner: self.inner.values(),
}
}
#[must_use]
pub fn iter_mut(&mut self) -> IterMut<'_, T> {
IterMut {
inner: self.inner.values_mut(),
}
}
pub fn clear(&mut self) {
self.inner.clear();
}
}
impl<T> Default for Region<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> core::fmt::Debug for Region<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Region")
.field("region_id", &self.region_id)
.field("len", &self.len())
.field("capacity", &self.capacity())
.finish()
}
}
impl<'a, T> IntoIterator for &'a Region<T> {
type Item = &'a T;
type IntoIter = Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a, T> IntoIterator for &'a mut Region<T> {
type Item = &'a mut T;
type IntoIter = IterMut<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
pub struct Iter<'a, T> {
inner: slotmap::basic::Values<'a, slotmap::DefaultKey, T>,
}
impl<'a, T> Iterator for Iter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<T> ExactSizeIterator for Iter<'_, T> {
fn len(&self) -> usize {
self.inner.len()
}
}
impl<T> core::iter::FusedIterator for Iter<'_, T> {}
impl<T> Clone for Iter<'_, T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<T> core::fmt::Debug for Iter<'_, T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Iter").field("len", &self.len()).finish()
}
}
pub struct IterMut<'a, T> {
inner: slotmap::basic::ValuesMut<'a, slotmap::DefaultKey, T>,
}
impl<'a, T> Iterator for IterMut<'a, T> {
type Item = &'a mut T;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<T> ExactSizeIterator for IterMut<'_, T> {
fn len(&self) -> usize {
self.inner.len()
}
}
impl<T> core::iter::FusedIterator for IterMut<'_, T> {}
impl<T> core::fmt::Debug for IterMut<'_, T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("IterMut").field("len", &self.len()).finish()
}
}