cleanup the slab types
- get rid of HeaderOrIdxPtr - add IndexPtrSlab - add UntypedArenaSlab - add to_untyped for converting a typed slab into an unsyped slab - remove TypedArenaPtr::new, as they shouldn't be created outside of this module
This commit is contained in:
164
src/arena.rs
164
src/arena.rs
@@ -296,14 +296,6 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + ArenaAllocated> TypedArenaPtr<T> {
|
impl<T: ?Sized + ArenaAllocated> TypedArenaPtr<T> {
|
||||||
/// # Safety
|
|
||||||
/// - the pointers referee type is correct, safe code depends on the correctness of the type argument
|
|
||||||
/// - the pointer is allocated in the arena
|
|
||||||
#[inline]
|
|
||||||
pub const unsafe fn new(data: *mut T::Payload) -> Self {
|
|
||||||
unsafe { TypedArenaPtr(ptr::NonNull::new_unchecked(data)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn as_ptr(&self) -> *mut T::Payload {
|
pub fn as_ptr(&self) -> *mut T::Payload {
|
||||||
self.0.as_ptr()
|
self.0.as_ptr()
|
||||||
@@ -398,14 +390,14 @@ pub trait ArenaAllocated {
|
|||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// - the caller must guarantee that the pointee type of UntypedArenaPtr is Self
|
/// - the caller must guarantee that the pointee type of UntypedArenaPtr is Self
|
||||||
|
/// - the pointer must be non-null
|
||||||
unsafe fn typed_ptr(ptr: UntypedArenaPtr) -> TypedArenaPtr<Self>
|
unsafe fn typed_ptr(ptr: UntypedArenaPtr) -> TypedArenaPtr<Self>
|
||||||
where
|
where
|
||||||
Self::Payload: Sized,
|
Self::Payload: Sized,
|
||||||
{
|
{
|
||||||
// safety:
|
TypedArenaPtr(NonNull::new_unchecked(
|
||||||
// - allocated in an arena as from an UntypedArenaPtr
|
ptr.payload_offset().cast_mut().cast::<Self::Payload>(),
|
||||||
// - caller guarantees the type is correct
|
))
|
||||||
unsafe { TypedArenaPtr::new(ptr.payload_offset().cast_mut().cast::<Self::Payload>()) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::missing_safety_doc)]
|
#[allow(clippy::missing_safety_doc)]
|
||||||
@@ -417,20 +409,14 @@ pub trait ArenaAllocated {
|
|||||||
let slab = Box::new(TypedAllocSlab {
|
let slab = Box::new(TypedAllocSlab {
|
||||||
slab: AllocSlab {
|
slab: AllocSlab {
|
||||||
next: arena.base.take(),
|
next: arena.base.take(),
|
||||||
#[cfg(target_pointer_width = "32")]
|
header: ArenaHeader::build_with(size as u64, Self::tag()),
|
||||||
_padding: 0,
|
|
||||||
header: HeaderOrIdxPtr {
|
|
||||||
header: ArenaHeader::build_with(size as u64, Self::tag()),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
payload: value,
|
payload: value,
|
||||||
});
|
});
|
||||||
|
|
||||||
let raw_box = Box::into_raw(slab);
|
let (allocated_ptr, untyped_slab) = slab.to_untyped();
|
||||||
// safety: Box::into_raw retuns a pointer to a valid allocation
|
|
||||||
let allocated_ptr = unsafe { TypedAllocSlab::to_typed_arena_ptr(raw_box) };
|
|
||||||
|
|
||||||
arena.base = Some(NonNull::new(raw_box.cast::<AllocSlab>()).unwrap());
|
arena.base = Some(untyped_slab);
|
||||||
|
|
||||||
allocated_ptr
|
allocated_ptr
|
||||||
}
|
}
|
||||||
@@ -655,94 +641,132 @@ impl ArenaAllocated for IndexPtr {
|
|||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// - the caller must guarantee that the pointee type of UntypedArenaPtr is T
|
/// - the caller must guarantee that the pointee type of UntypedArenaPtr is T
|
||||||
|
/// - the pointer must be non-null
|
||||||
unsafe fn typed_ptr(ptr: UntypedArenaPtr) -> TypedArenaPtr<Self> {
|
unsafe fn typed_ptr(ptr: UntypedArenaPtr) -> TypedArenaPtr<Self> {
|
||||||
unsafe { TypedArenaPtr::new(ptr.get_ptr().cast_mut().cast::<IndexPtr>()) }
|
TypedArenaPtr(NonNull::new_unchecked(
|
||||||
|
ptr.get_ptr().cast_mut().cast::<IndexPtr>(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn alloc(arena: &mut Arena, value: Self) -> TypedArenaPtr<Self> {
|
fn alloc(arena: &mut Arena, value: Self) -> TypedArenaPtr<Self> {
|
||||||
let slab = Box::new(AllocSlab {
|
let slab = Box::new(IndexPtrSlab {
|
||||||
next: arena.base.take(),
|
next: arena.base.take(),
|
||||||
#[cfg(target_pointer_width = "32")]
|
index_ptr: value,
|
||||||
_padding: 0,
|
|
||||||
header: HeaderOrIdxPtr { idx_ptr: value },
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let raw_box = Box::into_raw(slab);
|
let (allocated_ptr, untyped_slab) = slab.to_untyped();
|
||||||
let allocated_ptr =
|
arena.base = Some(untyped_slab);
|
||||||
unsafe { TypedArenaPtr::new(ptr::addr_of_mut!((*raw_box).header.idx_ptr)) };
|
|
||||||
arena.base = Some(NonNull::new(raw_box).unwrap());
|
|
||||||
allocated_ptr
|
allocated_ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// - ptr points to an allocated slab of the correct kind
|
/// - ptr points to an allocated slab of the correct kind
|
||||||
unsafe fn dealloc(ptr: NonNull<TypedAllocSlab<Self>>) {
|
unsafe fn dealloc(ptr: NonNull<TypedAllocSlab<Self>>) {
|
||||||
drop(unsafe { Box::from_raw(ptr.as_ptr().cast::<AllocSlab>()) });
|
drop(unsafe { Box::from_raw(ptr.as_ptr().cast::<IndexPtrSlab>()) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
union HeaderOrIdxPtr {
|
#[derive(Debug)]
|
||||||
|
pub struct AllocSlab {
|
||||||
|
next: Option<UntypedArenaSlab>,
|
||||||
header: ArenaHeader,
|
header: ArenaHeader,
|
||||||
idx_ptr: IndexPtr,
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct IndexPtrSlab {
|
||||||
|
next: Option<UntypedArenaSlab>,
|
||||||
|
index_ptr: IndexPtr,
|
||||||
}
|
}
|
||||||
|
|
||||||
const _: () = {
|
const _: () = {
|
||||||
if std::mem::size_of::<ArenaHeader>() != std::mem::size_of::<IndexPtr>() {
|
if std::mem::align_of::<AllocSlab>() < std::mem::align_of::<*const ()>() {
|
||||||
panic!("Size of ArenaHeader != IndexPtr")
|
panic!("alignment of AllocSlab is too low");
|
||||||
|
}
|
||||||
|
|
||||||
|
if std::mem::offset_of!(AllocSlab, header) % std::mem::align_of::<*const ()>() != 0 {
|
||||||
|
panic!("alignment of header not a multiple of pointers alignment");
|
||||||
|
}
|
||||||
|
|
||||||
|
if std::mem::offset_of!(AllocSlab, header) != std::mem::offset_of!(IndexPtrSlab, index_ptr) {
|
||||||
|
panic!("IndexPtrSlab.index_ptr and AllocSlab.header are at different offsets");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
impl Debug for HeaderOrIdxPtr {
|
impl IndexPtrSlab {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
#[inline]
|
||||||
unsafe { &self.header }.fmt(f)
|
pub fn to_untyped(self: Box<Self>) -> (TypedArenaPtr<IndexPtr>, UntypedArenaSlab) {
|
||||||
}
|
let raw_box = Box::into_raw(self);
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for HeaderOrIdxPtr {
|
// safety: the pointer from Box::into_raw fullfills addr_of_mut's saftey requirements
|
||||||
fn clone(&self) -> Self {
|
let index_ptr_ptr = unsafe { ptr::addr_of_mut!((*raw_box).index_ptr) };
|
||||||
// safety:
|
let allocated_ptr = TypedArenaPtr(
|
||||||
// - we created the pointer from a valid reference
|
// safety: the pointer points into a valid allocation so it is non null
|
||||||
// - both ArenaHeader and IndexPtr are plain old datatypes, i.e. no managed resources that need to be cloned
|
unsafe { NonNull::new_unchecked(index_ptr_ptr) },
|
||||||
unsafe { std::ptr::read(self) }
|
);
|
||||||
|
|
||||||
|
let untyped_arena = UntypedArenaSlab {
|
||||||
|
// safety: pointer from Box::into_raw is never null
|
||||||
|
slab: unsafe { NonNull::new_unchecked(raw_box.cast::<AllocSlab>()) },
|
||||||
|
};
|
||||||
|
|
||||||
|
(allocated_ptr, untyped_arena)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AllocSlab {
|
|
||||||
next: Option<NonNull<AllocSlab>>,
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
|
||||||
_padding: u32,
|
|
||||||
header: HeaderOrIdxPtr,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct TypedAllocSlab<T: ?Sized + ArenaAllocated> {
|
pub struct TypedAllocSlab<T: ?Sized + ArenaAllocated> {
|
||||||
slab: AllocSlab,
|
slab: AllocSlab,
|
||||||
payload: T::Payload,
|
payload: T::Payload,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + ArenaAllocated> TypedAllocSlab<T> {
|
impl<T: ?Sized + ArenaAllocated> TypedAllocSlab<T> {
|
||||||
|
pub fn tag(&self) -> ArenaHeaderTag {
|
||||||
|
self.slab.header.tag()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn payload(&mut self) -> &mut T::Payload {
|
pub fn payload(&mut self) -> &mut T::Payload {
|
||||||
&mut self.payload
|
&mut self.payload
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Safety
|
|
||||||
/// - ptr points to a valid allocation of Self
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn to_typed_arena_ptr(ptr: *mut Self) -> TypedArenaPtr<T> {
|
pub fn to_untyped(self: Box<Self>) -> (TypedArenaPtr<T>, UntypedArenaSlab) {
|
||||||
// safety:
|
let raw_box = Box::into_raw(self);
|
||||||
// - this is the arena allocation of corresponding type
|
|
||||||
unsafe { TypedArenaPtr::new(addr_of_mut!((*ptr).payload)) }
|
// safety: the pointer from Box::into_raw fullfills addr_of_mut's saftey requirements
|
||||||
|
let payload_ptr = unsafe { addr_of_mut!((*raw_box).payload) };
|
||||||
|
|
||||||
|
(
|
||||||
|
TypedArenaPtr(unsafe {
|
||||||
|
// safety: the pointer points into a valid allocation so it is non null
|
||||||
|
ptr::NonNull::new_unchecked(payload_ptr)
|
||||||
|
}),
|
||||||
|
UntypedArenaSlab {
|
||||||
|
// safety: pointer from Box::into_raw is never null
|
||||||
|
slab: unsafe { NonNull::new_unchecked(raw_box.cast::<AllocSlab>()) },
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct UntypedArenaSlab {
|
||||||
|
slab: NonNull<AllocSlab>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for UntypedArenaSlab {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { drop_slab_in_place(self.slab) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Arena {
|
pub struct Arena {
|
||||||
base: Option<NonNull<AllocSlab>>,
|
base: Option<UntypedArenaSlab>,
|
||||||
pub f64_tbl: Arc<F64Table>,
|
pub f64_tbl: Arc<F64Table>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -767,7 +791,7 @@ unsafe fn drop_slab_in_place(value: NonNull<AllocSlab>) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
match (unsafe { value.as_ref() }).header.header.tag() {
|
match (unsafe { value.as_ref() }).header.tag() {
|
||||||
ArenaHeaderTag::Integer => {
|
ArenaHeaderTag::Integer => {
|
||||||
drop_typed_slab_in_place!(Integer, value);
|
drop_typed_slab_in_place!(Integer, value);
|
||||||
}
|
}
|
||||||
@@ -839,18 +863,18 @@ unsafe fn drop_slab_in_place(value: NonNull<AllocSlab>) {
|
|||||||
|
|
||||||
impl Drop for Arena {
|
impl Drop for Arena {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
// we un-nest UntypedArenaSlab to prevent stackoverflow due to the recursive drop
|
||||||
|
|
||||||
let mut ptr = self.base.take();
|
let mut ptr = self.base.take();
|
||||||
|
|
||||||
while let Some(slab) = ptr {
|
while let Some(mut slab) = ptr {
|
||||||
unsafe {
|
ptr = unsafe { slab.slab.as_mut() }.next.take();
|
||||||
ptr = slab.as_ref().next;
|
drop(slab);
|
||||||
drop_slab_in_place(slab);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const_assert!(mem::size_of::<AllocSlab>() == 16);
|
const_assert!(mem::size_of::<AllocSlab>() <= 24);
|
||||||
const_assert!(mem::size_of::<OrderedFloat<f64>>() == 8);
|
const_assert!(mem::size_of::<OrderedFloat<f64>>() == 8);
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -710,6 +710,7 @@ impl UntypedArenaPtr {
|
|||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// - this UntypedArenaPtr actuall pointee type is T
|
/// - this UntypedArenaPtr actuall pointee type is T
|
||||||
|
/// - the pointer must be non-null
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn as_typed_ptr<T: ?Sized + ArenaAllocated>(self) -> TypedArenaPtr<T>
|
pub unsafe fn as_typed_ptr<T: ?Sized + ArenaAllocated>(self) -> TypedArenaPtr<T>
|
||||||
where
|
where
|
||||||
|
|||||||
Reference in New Issue
Block a user