use std::{cell, marker, mem, process, ptr};
use super::{self as collections, AllocationError};
const OCCUPIED: usize = 1;
const DETACHED: usize = 2;
const TERMINAL: usize = 4;
const FLAGS: usize = OCCUPIED | DETACHED | TERMINAL;
type Invariant<'owner> = marker::PhantomData<fn(&'owner ()) -> &'owner ()>;
struct Entry<T: Copy> {
value: cell::Cell<mem::MaybeUninit<T>>,
state: cell::Cell<*mut ()>,
}
struct Group<T: Copy> {
entries: Box<[Entry<T>]>,
free: cell::Cell<*mut Entry<T>>,
leased: cell::Cell<bool>,
next: cell::Cell<Option<ptr::NonNull<Group<T>>>>,
}
pub struct Arena<T: Copy> {
groups: Option<ptr::NonNull<Group<T>>>,
owner: marker::PhantomData<Box<Group<T>>>,
}
pub unsafe trait ArenaOwner<'owner, T: Copy> {
fn arena(self) -> ptr::NonNull<Arena<T>>;
}
#[repr(transparent)]
pub struct Slots<'owner, T: Copy> {
group: ptr::NonNull<Group<T>>,
owner: Invariant<'owner>,
}
#[must_use = "a reserved completion must be committed or released"]
#[repr(transparent)]
pub struct Reservation<'owner, T: Copy> {
key: Echo<T>,
owner: Invariant<'owner>,
}
#[must_use = "a live completion must reach terminal completion or be detached"]
#[repr(transparent)]
pub struct Lease<'owner, T: Copy> {
key: Echo<T>,
owner: Invariant<'owner>,
}
#[repr(transparent)]
pub struct Key<'owner, T: Copy> {
echo: Echo<T>,
owner: Invariant<'owner>,
}
#[repr(transparent)]
pub struct Echo<T: Copy>(ptr::NonNull<Entry<T>>);
pub struct Drain<'arena, T: Copy> {
arena: marker::PhantomData<&'arena Arena<T>>,
}
#[repr(transparent)]
pub struct Completion<'arena, T: Copy> {
key: Echo<T>,
arena: marker::PhantomData<&'arena Arena<T>>,
}
const _: () = {
assert!(mem::align_of::<Group<usize>>() >= 8);
assert!(mem::size_of::<Entry<usize>>() == 2 * mem::size_of::<usize>());
assert!(mem::size_of::<Echo<usize>>() == mem::size_of::<usize>());
assert!(mem::size_of::<Key<'static, usize>>() == mem::size_of::<usize>());
assert!(mem::size_of::<Lease<'static, usize>>() == mem::size_of::<usize>());
assert!(mem::size_of::<Option<Lease<'static, usize>>>() == mem::size_of::<usize>());
assert!(mem::size_of::<Slots<'static, usize>>() == mem::size_of::<usize>());
assert!(mem::size_of::<Completion<'static, usize>>() == mem::size_of::<usize>());
assert!(mem::size_of::<Drain<'static, usize>>() == 0);
assert!(mem::size_of::<Arena<usize>>() == mem::size_of::<usize>());
};
impl<T: Copy> Arena<T> {
pub const fn new() -> Self {
Self {
groups: None,
owner: marker::PhantomData,
}
}
pub fn try_slots<'owner>(
owner: impl ArenaOwner<'owner, T>,
capacity: usize,
) -> Result<Slots<'owner, T>, AllocationError> {
let mut arena = owner.arena();
unsafe { arena.as_mut() }.try_slots_inner(capacity)
}
pub fn drain(&self) -> Drain<'_, T> {
Drain {
arena: marker::PhantomData,
}
}
fn try_slots_inner<'owner>(
&mut self,
capacity: usize,
) -> Result<Slots<'owner, T>, AllocationError> {
let mut selected = None;
let mut retained = None;
let mut quiescent = None;
let mut cursor = self.groups.take();
while let Some(group) = Group::pop(&mut cursor) {
let group_ref = unsafe { group.as_ref() };
if !group_ref.is_quiescent() {
Group::push(&mut retained, group);
continue;
}
let len = group_ref.entries.len();
let better = selected.is_none_or(|selected: ptr::NonNull<Group<T>>| {
len < unsafe { selected.as_ref() }.entries.len()
});
if len >= capacity && better {
if let Some(previous) = selected.replace(group) {
Group::push(&mut quiescent, previous);
}
} else {
Group::push(&mut quiescent, group);
}
}
let group = match selected {
Some(group) => group,
None => match Group::try_new(capacity) {
Ok(group) => group,
Err(error) => {
self.groups = retained;
while let Some(group) = Group::pop(&mut quiescent) {
Group::push(&mut self.groups, group);
}
return Err(error);
}
},
};
while let Some(group) = Group::pop(&mut quiescent) {
unsafe { Group::drop_owned(group) };
}
unsafe { group.as_ref() }.prepare(capacity);
Group::push(&mut retained, group);
self.groups = retained;
Ok(Slots {
group,
owner: marker::PhantomData,
})
}
}
impl<T: Copy> Drop for Arena<T> {
fn drop(&mut self) {
while let Some(group) = Group::pop(&mut self.groups) {
unsafe { Group::drop_owned(group) };
}
}
}
impl<T: Copy> Default for Arena<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: Copy> Group<T> {
fn try_new(capacity: usize) -> Result<ptr::NonNull<Self>, AllocationError> {
let entries = collections::BoxSliceExt::try_box_with(capacity, |_| Entry {
value: cell::Cell::new(mem::MaybeUninit::uninit()),
state: cell::Cell::new(ptr::null_mut()),
})?;
let group = collections::BoxExt::try_box(Self {
entries,
free: cell::Cell::new(ptr::null_mut()),
leased: cell::Cell::new(false),
next: cell::Cell::new(None),
})?;
Ok(unsafe { ptr::NonNull::new_unchecked(Box::into_raw(group)) })
}
fn pop(list: &mut Option<ptr::NonNull<Self>>) -> Option<ptr::NonNull<Self>> {
let group = list.take()?;
*list = unsafe { group.as_ref() }.next.replace(None);
Some(group)
}
fn push(list: &mut Option<ptr::NonNull<Self>>, group: ptr::NonNull<Self>) {
let group_ref = unsafe { group.as_ref() };
debug_assert!(group_ref.next.get().is_none());
group_ref.next.set(list.take());
*list = Some(group);
}
unsafe fn drop_owned(group: ptr::NonNull<Self>) {
debug_assert!(unsafe { group.as_ref() }.next.get().is_none());
drop(unsafe { Box::from_raw(group.as_ptr()) });
}
fn is_quiescent(&self) -> bool {
!self.leased.get()
&& self
.entries
.iter()
.all(|entry| entry.state.get().addr() & OCCUPIED == 0)
}
fn prepare(&self, capacity: usize) {
debug_assert!(!self.leased.get());
debug_assert!(capacity <= self.entries.len());
let mut next: *mut Entry<T> = ptr::null_mut();
for entry in self.entries[..capacity].iter().rev() {
debug_assert_eq!(entry.state.get().addr() & OCCUPIED, 0);
entry.state.set(next.cast());
next = ptr::from_ref(entry).cast_mut();
}
self.free.set(next);
self.leased.set(true);
}
}
impl<'owner, T: Copy> Slots<'owner, T> {
pub fn reserve(&self, value: T) -> Option<Reservation<'owner, T>> {
let owner = self.group;
let group = unsafe { owner.as_ref() };
debug_assert!(group.leased.get());
let key = Echo(ptr::NonNull::new(group.free.get())?);
let entry = unsafe { key.0.as_ref() };
let next = entry.state.get().cast::<Entry<T>>();
debug_assert_eq!(next.addr() & OCCUPIED, 0);
group.free.set(next);
entry.value.set(mem::MaybeUninit::new(value));
entry.state.set(
owner
.as_ptr()
.cast::<()>()
.map_addr(|address| address | OCCUPIED),
);
Some(Reservation {
key,
owner: marker::PhantomData,
})
}
}
impl<T: Copy> Drop for Slots<'_, T> {
fn drop(&mut self) {
let group = unsafe { self.group.as_ref() };
debug_assert!(group.leased.get());
group.leased.set(false);
}
}
impl<'owner, T: Copy> Reservation<'owner, T> {
pub fn key(&self) -> Key<'owner, T> {
Key {
echo: self.key,
owner: marker::PhantomData,
}
}
pub fn commit(self) -> Lease<'owner, T> {
let this = mem::ManuallyDrop::new(self);
Lease {
key: this.key,
owner: marker::PhantomData,
}
}
}
impl<T: Copy> Drop for Reservation<'_, T> {
fn drop(&mut self) {
self.key.release();
}
}
impl<T: Copy> Lease<'_, T> {
pub fn key(&self) -> Key<'_, T> {
Key {
echo: self.key,
owner: marker::PhantomData,
}
}
pub fn value(&self) -> T {
self.key.value()
}
pub fn complete(self) -> T {
let this = mem::ManuallyDrop::new(self);
let value = this.key.value();
let state = unsafe { this.key.0.as_ref() }.state.get();
if state.addr() & TERMINAL == 0 {
process::abort();
}
this.key.release();
value
}
}
impl<T: Copy> Drop for Lease<'_, T> {
fn drop(&mut self) {
let entry = unsafe { self.key.0.as_ref() };
let state = entry.state.get();
if state.addr() & OCCUPIED == 0 {
process::abort();
}
if state.addr() & TERMINAL != 0 {
self.key.release();
} else {
entry
.state
.set(state.map_addr(|address| address | DETACHED));
}
}
}
impl<T: Copy> Key<'_, T> {
pub fn expose(self) -> usize {
self.echo.expose()
}
pub unsafe fn erase(self) -> Echo<T> {
self.echo
}
}
impl<'arena, T: Copy> Drain<'arena, T> {
pub fn complete(&self, key: Echo<T>) -> Completion<'_, T> {
Completion {
key,
arena: marker::PhantomData,
}
}
}
impl<T: Copy> Completion<'_, T> {
pub fn resolve(self, more: bool) -> T {
let value = self.key.value();
self.key.complete_external(more);
value
}
}
impl<T: Copy> Echo<T> {
pub fn expose(self) -> usize {
self.0.as_ptr().expose_provenance()
}
pub unsafe fn from_exposed(address: usize) -> Option<Self> {
if address == 0 || address & (mem::align_of::<Entry<T>>() - 1) != 0 {
return None;
}
Some(Self(unsafe {
ptr::NonNull::new_unchecked(ptr::with_exposed_provenance_mut(address))
}))
}
fn value(self) -> T {
unsafe { self.0.as_ref().value.get().assume_init() }
}
fn complete_external(self, more: bool) {
if more {
return;
}
let entry = unsafe { self.0.as_ref() };
let state = entry.state.get();
if state.addr() & OCCUPIED == 0 {
process::abort();
}
entry
.state
.set(state.map_addr(|address| address | TERMINAL));
if state.addr() & DETACHED != 0 {
self.release();
}
}
fn release(self) {
let entry = unsafe { self.0.as_ref() };
let state = entry.state.get();
if state.addr() & OCCUPIED == 0 {
process::abort();
}
let owner = state
.map_addr(|address| address & !FLAGS)
.cast::<Group<T>>();
let group = unsafe { &*owner };
entry.state.set(group.free.get().cast());
group.free.set(self.0.as_ptr());
}
}
impl<T: Copy> Clone for Echo<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: Copy> Copy for Echo<T> {}
impl<T: Copy> std::fmt::Debug for Echo<T> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.debug_tuple("Echo").field(&self.0).finish()
}
}
impl<T: Copy> PartialEq for Echo<T> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T: Copy> Eq for Echo<T> {}
impl<T: Copy> Clone for Key<'_, T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: Copy> Copy for Key<'_, T> {}
impl<T: Copy> std::fmt::Debug for Key<'_, T> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.debug_tuple("Key").field(&self.echo).finish()
}
}
impl<T: Copy> PartialEq for Key<'_, T> {
fn eq(&self, other: &Self) -> bool {
self.echo == other.echo
}
}
impl<T: Copy> Eq for Key<'_, T> {}