From: Bennet Bleßmann Date: Sun, 7 Jul 2024 13:47:50 +0000 (+0200) Subject: cleanup the slab types X-Git-Tag: v0.10.0~127^2~1 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=689632b51ca32c0ec163c91ef1ab24301c77ee16;p=scryer-prolog.git 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 --- diff --git a/src/arena.rs b/src/arena.rs index c532d24a..46d4d26a 100644 --- a/src/arena.rs +++ b/src/arena.rs @@ -296,14 +296,6 @@ where } impl TypedArenaPtr { - /// # 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] pub fn as_ptr(&self) -> *mut T::Payload { self.0.as_ptr() @@ -398,14 +390,14 @@ pub trait ArenaAllocated { /// # Safety /// - 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 where Self::Payload: Sized, { - // safety: - // - allocated in an arena as from an UntypedArenaPtr - // - caller guarantees the type is correct - unsafe { TypedArenaPtr::new(ptr.payload_offset().cast_mut().cast::()) } + TypedArenaPtr(NonNull::new_unchecked( + ptr.payload_offset().cast_mut().cast::(), + )) } #[allow(clippy::missing_safety_doc)] @@ -417,20 +409,14 @@ pub trait ArenaAllocated { let slab = Box::new(TypedAllocSlab { slab: AllocSlab { next: arena.base.take(), - #[cfg(target_pointer_width = "32")] - _padding: 0, - header: HeaderOrIdxPtr { - header: ArenaHeader::build_with(size as u64, Self::tag()), - }, + header: ArenaHeader::build_with(size as u64, Self::tag()), }, payload: value, }); - let raw_box = Box::into_raw(slab); - // safety: Box::into_raw retuns a pointer to a valid allocation - let allocated_ptr = unsafe { TypedAllocSlab::to_typed_arena_ptr(raw_box) }; + let (allocated_ptr, untyped_slab) = slab.to_untyped(); - arena.base = Some(NonNull::new(raw_box.cast::()).unwrap()); + arena.base = Some(untyped_slab); allocated_ptr } @@ -655,94 +641,132 @@ impl ArenaAllocated for IndexPtr { /// # Safety /// - 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 { - unsafe { TypedArenaPtr::new(ptr.get_ptr().cast_mut().cast::()) } + TypedArenaPtr(NonNull::new_unchecked( + ptr.get_ptr().cast_mut().cast::(), + )) } #[inline] fn alloc(arena: &mut Arena, value: Self) -> TypedArenaPtr { - let slab = Box::new(AllocSlab { + let slab = Box::new(IndexPtrSlab { next: arena.base.take(), - #[cfg(target_pointer_width = "32")] - _padding: 0, - header: HeaderOrIdxPtr { idx_ptr: value }, + index_ptr: value, }); - let raw_box = Box::into_raw(slab); - let allocated_ptr = - unsafe { TypedArenaPtr::new(ptr::addr_of_mut!((*raw_box).header.idx_ptr)) }; - arena.base = Some(NonNull::new(raw_box).unwrap()); + let (allocated_ptr, untyped_slab) = slab.to_untyped(); + arena.base = Some(untyped_slab); allocated_ptr } /// # Safety /// - ptr points to an allocated slab of the correct kind unsafe fn dealloc(ptr: NonNull>) { - drop(unsafe { Box::from_raw(ptr.as_ptr().cast::()) }); + drop(unsafe { Box::from_raw(ptr.as_ptr().cast::()) }); } } #[repr(C)] -union HeaderOrIdxPtr { +#[derive(Debug)] +pub struct AllocSlab { + next: Option, header: ArenaHeader, - idx_ptr: IndexPtr, +} + +#[repr(C)] +#[derive(Debug)] +pub struct IndexPtrSlab { + next: Option, + index_ptr: IndexPtr, } const _: () = { - if std::mem::size_of::() != std::mem::size_of::() { - panic!("Size of ArenaHeader != IndexPtr") + if std::mem::align_of::() < std::mem::align_of::<*const ()>() { + panic!("alignment of AllocSlab is too low"); } -}; -impl Debug for HeaderOrIdxPtr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - unsafe { &self.header }.fmt(f) + if std::mem::offset_of!(AllocSlab, header) % std::mem::align_of::<*const ()>() != 0 { + panic!("alignment of header not a multiple of pointers alignment"); } -} -impl Clone for HeaderOrIdxPtr { - fn clone(&self) -> Self { - // safety: - // - we created the pointer from a valid reference - // - both ArenaHeader and IndexPtr are plain old datatypes, i.e. no managed resources that need to be cloned - unsafe { std::ptr::read(self) } + 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"); } -} +}; -#[repr(C)] -#[derive(Clone, Debug)] -pub struct AllocSlab { - next: Option>, - #[cfg(target_pointer_width = "32")] - _padding: u32, - header: HeaderOrIdxPtr, +impl IndexPtrSlab { + #[inline] + pub fn to_untyped(self: Box) -> (TypedArenaPtr, UntypedArenaSlab) { + let raw_box = Box::into_raw(self); + + // safety: the pointer from Box::into_raw fullfills addr_of_mut's saftey requirements + let index_ptr_ptr = unsafe { ptr::addr_of_mut!((*raw_box).index_ptr) }; + let allocated_ptr = TypedArenaPtr( + // safety: the pointer points into a valid allocation so it is non null + unsafe { NonNull::new_unchecked(index_ptr_ptr) }, + ); + + let untyped_arena = UntypedArenaSlab { + // safety: pointer from Box::into_raw is never null + slab: unsafe { NonNull::new_unchecked(raw_box.cast::()) }, + }; + + (allocated_ptr, untyped_arena) + } } #[repr(C)] -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct TypedAllocSlab { slab: AllocSlab, payload: T::Payload, } impl TypedAllocSlab { + pub fn tag(&self) -> ArenaHeaderTag { + self.slab.header.tag() + } + pub fn payload(&mut self) -> &mut T::Payload { &mut self.payload } - /// # Safety - /// - ptr points to a valid allocation of Self #[inline] - pub unsafe fn to_typed_arena_ptr(ptr: *mut Self) -> TypedArenaPtr { - // safety: - // - this is the arena allocation of corresponding type - unsafe { TypedArenaPtr::new(addr_of_mut!((*ptr).payload)) } + pub fn to_untyped(self: Box) -> (TypedArenaPtr, UntypedArenaSlab) { + let raw_box = Box::into_raw(self); + + // 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::()) }, + }, + ) + } +} + +#[derive(Debug)] +#[repr(transparent)] +pub struct UntypedArenaSlab { + slab: NonNull, +} + +impl Drop for UntypedArenaSlab { + fn drop(&mut self) { + unsafe { drop_slab_in_place(self.slab) }; } } #[derive(Debug)] pub struct Arena { - base: Option>, + base: Option, pub f64_tbl: Arc, } @@ -767,7 +791,7 @@ unsafe fn drop_slab_in_place(value: NonNull) { }; } - match (unsafe { value.as_ref() }).header.header.tag() { + match (unsafe { value.as_ref() }).header.tag() { ArenaHeaderTag::Integer => { drop_typed_slab_in_place!(Integer, value); } @@ -839,18 +863,18 @@ unsafe fn drop_slab_in_place(value: NonNull) { impl Drop for Arena { fn drop(&mut self) { + // we un-nest UntypedArenaSlab to prevent stackoverflow due to the recursive drop + let mut ptr = self.base.take(); - while let Some(slab) = ptr { - unsafe { - ptr = slab.as_ref().next; - drop_slab_in_place(slab); - } + while let Some(mut slab) = ptr { + ptr = unsafe { slab.slab.as_mut() }.next.take(); + drop(slab); } } } -const_assert!(mem::size_of::() == 16); +const_assert!(mem::size_of::() <= 24); const_assert!(mem::size_of::>() == 8); #[cfg(test)] diff --git a/src/types.rs b/src/types.rs index 07328a07..49ff0a6e 100644 --- a/src/types.rs +++ b/src/types.rs @@ -710,6 +710,7 @@ impl UntypedArenaPtr { /// # Safety /// - this UntypedArenaPtr actuall pointee type is T + /// - the pointer must be non-null #[inline] pub unsafe fn as_typed_ptr(self) -> TypedArenaPtr where