use std::sync::atomic::{AtomicUsize, Ordering};
use crate::chunked_storage::ChunkedStorage;
use crate::{Checkpoint, Idx};
pub struct SharedArena<T> {
storage: ChunkedStorage<T>,
reserved: AtomicUsize,
published: AtomicUsize,
}
impl<T> SharedArena<T> {
#[must_use]
pub const fn new() -> Self {
Self {
storage: ChunkedStorage::new(),
reserved: AtomicUsize::new(0),
published: AtomicUsize::new(0),
}
}
pub fn alloc(&self, value: T) -> Idx<T> {
let slot = self.reserved.fetch_add(1, Ordering::Relaxed);
let ok = self.storage.set(slot, value);
assert!(ok, "slot {slot} already occupied");
self.advance_published(slot);
Idx::from_raw(slot)
}
fn advance_published(&self, slot: usize) {
loop {
let p = self.published.load(Ordering::Acquire);
if p > slot {
break; }
if !self.storage.is_set(p) {
std::hint::spin_loop();
continue;
}
let _ = self.published.compare_exchange_weak(
p,
p + 1,
Ordering::Release,
Ordering::Relaxed,
);
}
}
#[must_use]
pub fn get(&self, idx: Idx<T>) -> &T {
let i = idx.into_raw();
assert!(
i < self.published.load(Ordering::Acquire),
"index out of bounds: index is {i} but published length is {}",
self.published.load(Ordering::Acquire),
);
self.storage.get(i)
}
#[must_use]
pub fn len(&self) -> usize {
self.published.load(Ordering::Acquire)
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[must_use]
pub fn checkpoint(&self) -> Checkpoint<T> {
Checkpoint::from_len(self.published.load(Ordering::Acquire))
}
pub fn rollback(&mut self, cp: Checkpoint<T>) {
let current = *self.published.get_mut();
assert!(
cp.len() <= current,
"checkpoint {} beyond current length {}",
cp.len(),
current,
);
for slot in (cp.len()..current).rev() {
self.storage.take(slot);
}
*self.published.get_mut() = cp.len();
*self.reserved.get_mut() = cp.len();
}
pub fn reset(&mut self) {
let current = *self.published.get_mut();
for slot in (0..current).rev() {
self.storage.take(slot);
}
*self.published.get_mut() = 0;
*self.reserved.get_mut() = 0;
}
#[must_use]
pub fn is_valid(&self, idx: Idx<T>) -> bool {
idx.into_raw() < self.published.load(Ordering::Acquire)
}
#[must_use]
pub fn try_get(&self, idx: Idx<T>) -> Option<&T> {
let i = idx.into_raw();
if i < self.published.load(Ordering::Acquire) {
Some(self.storage.get(i))
} else {
None
}
}
pub fn alloc_extend(&self, iter: impl IntoIterator<Item = T>) -> Option<Idx<T>> {
let mut first = None;
for value in iter {
let idx = self.alloc(value);
if first.is_none() {
first = Some(idx);
}
}
first
}
#[must_use]
pub fn iter(&self) -> SharedArenaIter<'_, T> {
SharedArenaIter {
storage: &self.storage,
pos: 0,
len: self.published.load(Ordering::Acquire),
}
}
#[must_use]
pub fn iter_indexed(&self) -> SharedArenaIterIndexed<'_, T> {
SharedArenaIterIndexed {
storage: &self.storage,
pos: 0,
len: self.published.load(Ordering::Acquire),
}
}
pub fn drain(&mut self) -> std::vec::IntoIter<T> {
let current = *self.published.get_mut();
let mut items = Vec::with_capacity(current);
for slot in 0..current {
if let Some(value) = self.storage.take(slot) {
items.push(value);
}
}
*self.published.get_mut() = 0;
*self.reserved.get_mut() = 0;
items.into_iter()
}
}
impl<T> Default for SharedArena<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> std::ops::Index<Idx<T>> for SharedArena<T> {
type Output = T;
fn index(&self, idx: Idx<T>) -> &T {
self.get(idx)
}
}
impl<'a, T> IntoIterator for &'a SharedArena<T> {
type Item = &'a T;
type IntoIter = SharedArenaIter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<T> IntoIterator for SharedArena<T> {
type Item = T;
type IntoIter = std::vec::IntoIter<T>;
fn into_iter(mut self) -> Self::IntoIter {
self.drain()
}
}
impl<T> Extend<T> for SharedArena<T> {
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
for value in iter {
self.alloc(value);
}
}
}
impl<T> std::iter::FromIterator<T> for SharedArena<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let mut arena = Self::new();
arena.extend(iter);
arena
}
}
pub struct SharedArenaIter<'a, T> {
storage: &'a ChunkedStorage<T>,
pos: usize,
len: usize,
}
impl<'a, T> Iterator for SharedArenaIter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
if self.pos < self.len {
let val = self.storage.get(self.pos);
self.pos += 1;
Some(val)
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.len - self.pos;
(remaining, Some(remaining))
}
}
impl<T> ExactSizeIterator for SharedArenaIter<'_, T> {}
pub struct SharedArenaIterIndexed<'a, T> {
storage: &'a ChunkedStorage<T>,
pos: usize,
len: usize,
}
impl<'a, T> Iterator for SharedArenaIterIndexed<'a, T> {
type Item = (Idx<T>, &'a T);
fn next(&mut self) -> Option<Self::Item> {
if self.pos < self.len {
let idx = Idx::from_raw(self.pos);
let val = self.storage.get(self.pos);
self.pos += 1;
Some((idx, val))
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.len - self.pos;
(remaining, Some(remaining))
}
}
impl<T> ExactSizeIterator for SharedArenaIterIndexed<'_, T> {}