use super::chunk::ChunkRef;
use super::options::{Bool, ChunkSizePriv, SupportsPositionsPriv};
use super::ArenaOptions;
use alloc::alloc::handle_alloc_error;
use alloc::boxed::Box;
use alloc::sync::Arc;
use core::fmt::{Debug, Display};
use core::hint::unreachable_unchecked;
use core::marker::PhantomData;
use core::mem;
use core::ptr::{self, NonNull};
pub(crate) mod iter;
use iter::{IntoIter, Iter, IterMut, IterPtr, Position};
type Array<T, Options> =
<<Options as ArenaOptions<T>>::ChunkSize as ChunkSizePriv<T>>::Array;
type SupportsPositions<T, Options> =
<Options as ArenaOptions<T>>::SupportsPositions;
type ArenaRc<T, Options> =
<SupportsPositions<T, Options> as SupportsPositionsPriv>::Rc;
type ArenaChunk<T, Options> = ChunkRef<T, Array<T, Options>>;
fn valid_rc_update<T>(old: &Option<Arc<T>>, new: &Option<Arc<T>>) -> bool {
match (old, new) {
(Some(old), Some(new)) => Arc::ptr_eq(old, new),
(Some(_old), None) => false,
(None, _new) => true,
}
}
pub struct ManuallyDropArena<T, Options: ArenaOptions<T> = super::Options> {
rc: Option<ArenaRc<T, Options>>,
head: Option<ArenaChunk<T, Options>>,
tail: Option<ArenaChunk<T, Options>>,
tail_len: usize,
len: usize,
phantom: PhantomData<Box<T>>,
}
impl<T, Options: ArenaOptions<T>> Default for ManuallyDropArena<T, Options> {
fn default() -> Self {
Self::new()
}
}
impl<T, Options: ArenaOptions<T>> ManuallyDropArena<T, Options> {
const CHUNK_SIZE: usize = ArenaChunk::<T, Options>::CAPACITY;
pub fn new() -> Self {
Self {
rc: None,
head: None,
tail: None,
tail_len: Self::CHUNK_SIZE,
len: 0,
phantom: PhantomData,
}
}
fn ensure_free_space(&mut self) -> Result<(), impl Debug + Display> {
assert!(
Self::CHUNK_SIZE > 0,
"cannot allocate items when chunk size is 0",
);
if self.tail_len < Self::CHUNK_SIZE {
return Ok(());
}
let chunk = if let Some(chunk) = ChunkRef::new(self.tail.take()) {
chunk
} else {
return Err("could not allocate chunk");
};
self.head.get_or_insert_with(|| chunk.clone());
self.tail = Some(chunk);
self.tail_len = 0;
Ok(())
}
fn alloc_ptr(&mut self, value: T) -> NonNull<T> {
self.try_alloc_ptr(value).unwrap_or_else(|| {
handle_alloc_error(ArenaChunk::<T, Options>::LAYOUT);
})
}
fn try_alloc_ptr(&mut self, value: T) -> Option<NonNull<T>> {
self.ensure_free_space().ok()?;
SupportsPositions::<T, Options>::init_rc(&mut self.rc);
let chunk = self.tail.as_mut().unwrap_or_else(|| {
unsafe { unreachable_unchecked() }
});
let item = unsafe { chunk.get(self.tail_len) };
unsafe {
item.as_ptr().write(value);
}
self.tail_len += 1;
self.len += 1;
Some(item)
}
pub unsafe fn drop(&mut self) {
let mut head = if let Some(head) = self.head.take() {
head
} else {
return;
};
self.tail = None;
let tail_len = mem::replace(&mut self.tail_len, Self::CHUNK_SIZE);
self.len = 0;
self.rc = None;
while let Some(next) = head.next() {
unsafe {
head.drop_all();
}
unsafe {
head.dealloc();
}
head = next;
}
for i in 0..tail_len {
unsafe {
head.drop_item(i);
}
}
unsafe {
head.dealloc();
}
}
pub unsafe fn manually_drop(&mut self) {
unsafe {
self.drop();
}
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn alloc<'a>(&mut self, value: T) -> &'a mut T
where
Options: 'a + ArenaOptions<T, Mutable = Bool<true>>,
{
unsafe { self.alloc_ptr(value).as_mut() }
}
pub fn try_alloc<'a>(&mut self, value: T) -> Option<&'a mut T>
where
Options: 'a + ArenaOptions<T, Mutable = Bool<true>>,
{
Some(unsafe { self.try_alloc_ptr(value)?.as_mut() })
}
pub fn alloc_shared<'a>(&mut self, value: T) -> &'a T
where
Options: 'a,
{
unsafe { self.alloc_ptr(value).as_ref() }
}
pub fn try_alloc_shared<'a>(&mut self, value: T) -> Option<&'a T>
where
Options: 'a,
{
Some(unsafe { self.try_alloc_ptr(value)?.as_ref() })
}
fn end(&self) -> *const T {
self.tail.as_ref().map_or(ptr::null(), |c| {
unsafe { c.get(self.tail_len) }.as_ptr()
})
}
fn iter_ptr<const DROP: bool>(&self) -> IterPtr<T, Options, DROP> {
IterPtr {
chunk: self.head.clone(),
index: 0,
end: self.end(),
rc: self.rc.clone(),
phantom: PhantomData,
}
}
pub fn iter<'a>(&self) -> Iter<'a, T, Options>
where
Options: ArenaOptions<T, Mutable = Bool<false>>,
{
unsafe { self.iter_unchecked() }
}
pub unsafe fn iter_unchecked<'a>(&self) -> Iter<'a, T, Options> {
Iter {
inner: self.iter_ptr::<false>(),
phantom: PhantomData,
}
}
pub unsafe fn iter_mut_unchecked<'a>(
&mut self,
) -> IterMut<'a, T, Options> {
IterMut {
inner: self.iter_ptr::<false>(),
phantom: PhantomData,
}
}
pub unsafe fn into_iter_unchecked(self) -> IntoIter<T, Options> {
IntoIter(self.iter_ptr())
}
}
impl<T, Options> ManuallyDropArena<T, Options>
where
Options: ArenaOptions<T, SupportsPositions = Bool<true>>,
{
fn iter_ptr_at(&self, position: &Position) -> IterPtr<T, Options> {
assert!(
valid_rc_update(&position.rc, &self.rc),
"`position` is not part of this arena",
);
let chunk = position.chunk.map(|p| unsafe { ChunkRef::from_ptr(p) });
IterPtr {
chunk: chunk.or_else(|| self.head.clone()),
index: position.index,
end: self.end(),
rc: self.rc.clone(),
phantom: PhantomData,
}
}
pub fn iter_at<'a>(&self, position: &Position) -> Iter<'a, T, Options>
where
Options: ArenaOptions<T, Mutable = Bool<false>>,
{
unsafe { self.iter_at_unchecked(position) }
}
pub unsafe fn iter_at_unchecked<'a>(
&self,
position: &Position,
) -> Iter<'a, T, Options> {
Iter {
inner: self.iter_ptr_at(position),
phantom: PhantomData,
}
}
pub unsafe fn iter_mut_at_unchecked<'a>(
&mut self,
position: &Position,
) -> IterMut<'a, T, Options> {
IterMut {
inner: self.iter_ptr_at(position),
phantom: PhantomData,
}
}
}
unsafe impl<T, Options> Sync for ManuallyDropArena<T, Options>
where
T: Sync,
Options: ArenaOptions<T>,
{
}
unsafe impl<T, Options> Send for ManuallyDropArena<T, Options>
where
T: Send + Sync,
Options: ArenaOptions<T>,
{
}