use core::alloc::Layout;
use core::cell::UnsafeCell;
use core::marker::PhantomData;
use core::mem::{self, size_of};
use core::ptr::{self, drop_in_place};
use std::vec;
use crate::UninitAllocator;
#[derive(Debug)]
pub struct TypedArenaRef<'a, T, A: UninitAllocator + 'a> {
allocator: &'a A,
allocations: UnsafeCell<Vec<*mut T>>,
_marker: PhantomData<*const ()>,
}
impl<'a, T, A: UninitAllocator + 'a> TypedArenaRef<'a, T, A> {
pub fn new_in(allocator: &'a A) -> Self {
Self { allocator, allocations: UnsafeCell::new(Vec::new()), _marker: PhantomData }
}
pub fn alloc(&self, value: T) -> &mut T {
self.alloc_impl(value).expect("TypedArenaRef allocation failed")
}
pub fn try_alloc(&self, value: T) -> Option<&mut T> {
self.alloc_impl(value)
}
#[allow(clippy::mut_from_ref)]
fn alloc_impl(&self, value: T) -> Option<&mut T> {
if size_of::<T>() == 0 {
unsafe {
let dangling = ptr::NonNull::<T>::dangling();
dangling.as_ptr().write(value);
return Some(&mut *dangling.as_ptr());
}
}
let layout = Layout::new::<T>();
let slice = self.allocator.try_alloc_uninit(layout)?;
let ptr = slice.as_mut_ptr().cast::<T>();
unsafe {
ptr.write(value);
(*self.allocations.get()).push(ptr);
Some(&mut *ptr)
}
}
pub fn allocator(&self) -> &A {
self.allocator
}
pub fn len(&self) -> usize {
unsafe { (*self.allocations.get()).len() }
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn reset(&mut self) {
let allocs = self.allocations.get_mut();
for &ptr in allocs.iter().rev() {
unsafe {
drop_in_place(ptr);
}
}
allocs.clear();
}
}
impl<'a, T, A: UninitAllocator + 'a> Drop for TypedArenaRef<'a, T, A> {
fn drop(&mut self) {
self.reset();
}
}
pub struct IntoIter<'a, T, A: UninitAllocator + 'a> {
allocator: &'a A,
pointers: vec::IntoIter<*mut T>,
}
impl<'a, T, A: UninitAllocator + 'a> IntoIter<'a, T, A> {
#[must_use]
pub fn allocator(&self) -> &A {
self.allocator
}
}
impl<'a, T, A: UninitAllocator + 'a> Iterator for IntoIter<'a, T, A> {
type Item = T;
fn next(&mut self) -> Option<T> {
let ptr = self.pointers.next()?;
Some(unsafe { ptr::read(ptr) })
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.pointers.size_hint()
}
}
impl<'a, T, A: UninitAllocator + 'a> Drop for IntoIter<'a, T, A> {
fn drop(&mut self) {
for ptr in self.pointers.by_ref() {
unsafe {
ptr::drop_in_place(ptr);
}
}
}
}
impl<'a, T, A: UninitAllocator + 'a> IntoIterator for TypedArenaRef<'a, T, A> {
type Item = T;
type IntoIter = IntoIter<'a, T, A>;
fn into_iter(self) -> IntoIter<'a, T, A> {
let this = mem::ManuallyDrop::new(self);
let allocations = unsafe { ptr::read(this.allocations.get()) };
let pointers: Vec<*mut T> = allocations.into_iter().rev().collect();
IntoIter { allocator: this.allocator, pointers: pointers.into_iter() }
}
}
impl<'a, T, A: UninitAllocator + 'a> IntoIterator for &'a TypedArenaRef<'a, T, A> {
type Item = &'a T;
type IntoIter = Iter<'a, T>;
fn into_iter(self) -> Iter<'a, T> {
self.iter()
}
}
impl<'a, T, A: UninitAllocator + 'a> IntoIterator for &'a mut TypedArenaRef<'a, T, A> {
type Item = &'a mut T;
type IntoIter = IterMut<'a, T>;
fn into_iter(self) -> IterMut<'a, T> {
self.iter_mut()
}
}
impl<'a, T, A: UninitAllocator + 'a> DoubleEndedIterator for IntoIter<'a, T, A> {
fn next_back(&mut self) -> Option<T> {
let ptr = self.pointers.next_back()?;
Some(unsafe { ptr::read(ptr) })
}
}
pub struct Iter<'s, T> {
pointers: vec::IntoIter<*const T>,
_marker: PhantomData<&'s ()>,
}
impl<'s, T: 's> Iterator for Iter<'s, T> {
type Item = &'s T;
fn next(&mut self) -> Option<&'s T> {
let ptr = self.pointers.next()?;
Some(unsafe { &*ptr })
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.pointers.size_hint()
}
}
impl<'s, T: 's> DoubleEndedIterator for Iter<'s, T> {
fn next_back(&mut self) -> Option<&'s T> {
let ptr = self.pointers.next_back()?;
Some(unsafe { &*ptr })
}
}
pub struct IterMut<'s, T> {
slice: &'s [*mut T],
pos: usize,
}
impl<'s, T> Iterator for IterMut<'s, T> {
type Item = &'s mut T;
fn next(&mut self) -> Option<&'s mut T> {
if self.pos == 0 {
return None;
}
self.pos -= 1;
let ptr = self.slice[self.pos];
Some(unsafe { &mut *ptr })
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.pos, Some(self.pos))
}
}
impl<'a, T, A: UninitAllocator + 'a> TypedArenaRef<'a, T, A> {
pub fn iter(&self) -> Iter<'_, T> {
let allocations = unsafe { &*self.allocations.get() };
let mut pointers: Vec<*const T> =
allocations.iter().copied().map(<*mut T>::cast_const).collect();
pointers.reverse();
Iter { pointers: pointers.into_iter(), _marker: PhantomData }
}
pub fn iter_mut(&mut self) -> IterMut<'_, T> {
let allocs = self.allocations.get_mut();
let len = allocs.len();
IterMut { slice: allocs.as_slice(), pos: len }
}
}