use once_cell::sync::Lazy;
use std::{
cell::UnsafeCell,
mem::{self, forget, MaybeUninit},
ops::{Deref, DerefMut},
ptr::addr_of,
sync::atomic::{AtomicU64, Ordering},
};
const IDX: usize = (1 << 8) - 1;
const IDX_MASK: usize = !IDX;
#[repr(align(64))]
pub struct Arena64<T> {
occupancy: AtomicU64,
slots: [UnsafeCell<MaybeUninit<T>>; 64],
next: Lazy<Box<Arena64<T>>>,
}
impl<T> Arena64<T> {
pub const fn new() -> Self {
let slots = unsafe { MaybeUninit::uninit().assume_init() };
Arena64 {
occupancy: AtomicU64::new(0),
slots,
next: Lazy::new(|| Box::new(Arena64::new())),
}
}
pub fn insert<'a>(&'a self, value: T) -> Slot<'a, T> {
let mut occupancy = self.occupancy.load(Ordering::Acquire);
let idx = loop {
let least_significant_bit = !occupancy & (occupancy.wrapping_add(1));
if least_significant_bit.ne(&0) {
occupancy = self
.occupancy
.fetch_or(least_significant_bit, Ordering::AcqRel);
if (occupancy & least_significant_bit).eq(&0) {
break least_significant_bit.trailing_zeros();
}
} else {
return self.next.insert(value);
}
};
unsafe {
*self.slots[idx as usize].get() = MaybeUninit::new(value);
}
Slot {
arena: self,
idx: idx as usize,
}
}
}
unsafe impl<T> Send for Arena64<T> where T: Send {}
unsafe impl<T> Sync for Arena64<T> where T: Sync {}
pub struct Slot<'a, T> {
arena: &'a Arena64<T>,
idx: usize,
}
impl<'a, T> Slot<'a, T> {
pub fn take(self) -> T {
let value = unsafe {
mem::replace(
&mut *self.arena.slots[self.idx].get(),
MaybeUninit::uninit(),
)
.assume_init()
};
self.arena
.occupancy
.fetch_add(!(1 << self.idx), Ordering::Release);
forget(self);
value
}
pub unsafe fn from_raw(ptr: *mut ()) -> Self {
Self {
arena: &*((ptr as usize & IDX_MASK) as *const Arena64<T>),
idx: ptr as usize * IDX,
}
}
pub unsafe fn into_raw(self) -> *mut () {
((addr_of!(*self.arena) as usize) | self.idx) as *mut ()
}
}
unsafe impl<'a, T> Send for Slot<'a, T> where T: Send {}
unsafe impl<'a, T> Sync for Slot<'a, T> where T: Sync {}
impl<'a, T> Deref for Slot<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { (&*self.arena.slots[self.idx].get()).assume_init_ref() }
}
}
impl<'a, T> DerefMut for Slot<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { (&mut *self.arena.slots[self.idx].get()).assume_init_mut() }
}
}
impl<'a, T> Drop for Slot<'a, T> {
fn drop(&mut self) {
unsafe { (&mut *self.arena.slots[self.idx].get()).assume_init_drop() }
self.arena
.occupancy
.fetch_add(!(1 << self.idx), Ordering::Release);
}
}
#[cfg(test)]
mod tests {
use crate::{Arena64, Slot};
#[test]
fn it_grows() {
let arena = Arena64::new();
let slots: Vec<Slot<u32>> = (0..512).map(|i| arena.insert(i)).collect();
let values: Vec<u32> = slots.into_iter().map(|slot| slot.take()).collect();
assert_eq!(values, (0..512).collect::<Vec<u32>>())
}
}