From: Emilie Burgun Date: Sun, 10 May 2026 11:53:58 +0000 (+0200) Subject: RawBlock: seal most fields, replace `top` with a `capacity` field X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=3c5818a040a70dbe2dc12a80bae428b93213b699;p=scryer-prolog.git RawBlock: seal most fields, replace `top` with a `capacity` field --- diff --git a/src/machine/stack.rs b/src/machine/stack.rs index ad9b4755..fe57e91d 100644 --- a/src/machine/stack.rs +++ b/src/machine/stack.rs @@ -183,7 +183,7 @@ impl Stack { let frame_size = AndFrame::size_of(num_cells); unsafe { - let e = (*self.buf.ptr.get_mut()).addr() - self.buf.base.addr(); + let e = self.buf.used_bytes(); let new_ptr = self.alloc(frame_size)?; let mut offset = prelude_size::(); @@ -209,14 +209,14 @@ impl Stack { } pub(crate) fn top(&self) -> usize { - unsafe { (*self.buf.ptr.get()).addr() - self.buf.base.addr() } + self.buf.used_bytes() } pub(crate) fn allocate_or_frame(&mut self, num_cells: usize) -> Result { let frame_size = OrFrame::size_of(num_cells); unsafe { - let b = (*self.buf.ptr.get_mut()).addr() - self.buf.base.addr(); + let b = self.buf.used_bytes(); let new_ptr = self.alloc(frame_size)?; let mut offset = prelude_size::(); @@ -267,11 +267,7 @@ impl Stack { #[inline(always)] pub(crate) fn truncate(&mut self, b: usize) { - let base = unsafe { self.buf.base.add(b) }; - - if base < (*self.buf.ptr.get_mut()) { - *self.buf.ptr.get_mut() = base.cast_mut(); - } + self.buf.shrink(b); } } diff --git a/src/raw_block.rs b/src/raw_block.rs index 64346883..8c4e4af5 100644 --- a/src/raw_block.rs +++ b/src/raw_block.rs @@ -11,11 +11,13 @@ pub trait RawBlockTraits { fn align() -> usize; } +/// A block of memory with fast, lock-free appends. #[derive(Debug)] pub struct RawBlock { pub base: *const u8, - pub top: *const u8, - pub ptr: UnsafeCell<*mut u8>, + capacity: usize, + + ptr: UnsafeCell<*mut u8>, _marker: PhantomData, } @@ -24,7 +26,7 @@ impl RawBlock { pub fn empty_block() -> Self { RawBlock { base: ptr::null(), - top: ptr::null(), + capacity: 0, ptr: UnsafeCell::new(ptr::null_mut()), _marker: PhantomData, } @@ -48,7 +50,7 @@ impl RawBlock { return Err(AllocError); } self.base = new_base; - self.top = self.base.add(cap); + self.capacity = cap; *self.ptr.get_mut() = self.base.cast_mut(); Ok(()) } @@ -57,7 +59,7 @@ impl RawBlock { if self.base.is_null() { self.init_at_size(T::init_size()) } else { - let size = self.size(); + let size = self.capacity(); let layout = alloc::Layout::from_size_align_unchecked(size, T::align()); let new_base = alloc::realloc(self.base.cast_mut(), layout, size * 2).cast_const(); @@ -65,46 +67,69 @@ impl RawBlock { Err(AllocError) } else { self.base = new_base; - self.top = self.base.add(size * 2); - *self.ptr.get_mut() = self.base.add(size).cast_mut(); + self.capacity = size * 2; + *self.ptr.get_mut() = (self.base as usize + size) as *mut _; Ok(()) } } } pub unsafe fn grow_new(&self) -> Result { + self.debug_check_invariants(); if self.base.is_null() { Self::new() } else { let mut new_block = Self::empty_block(); - new_block.init_at_size(self.size() * 2)?; - let allocated = (*self.ptr.get()).addr() - self.base.addr(); + new_block.init_at_size(self.capacity() * 2)?; + let allocated = self.used_bytes(); self.base.copy_to(new_block.base.cast_mut(), allocated); *new_block.ptr.get_mut() = new_block.base.add(allocated).cast_mut(); + + new_block.debug_check_invariants(); + Ok(new_block) } } + #[inline(always)] + fn debug_check_invariants(&self) { + if cfg!(debug_assertions) { + unsafe { + assert!( + *self.ptr.get() as *const _ >= self.base, + "self.ptr = {:?} < {:?} = self.base", + *self.ptr.get(), + self.base + ); + } + + assert!(self.used_bytes() <= self.capacity()); + } + } + #[inline] - pub fn size(&self) -> usize { - self.top.addr() - self.base.addr() + pub fn capacity(&self) -> usize { + self.capacity } - #[inline(always)] - unsafe fn free_space(&self) -> usize { - debug_assert!( - *self.ptr.get() as *const _ >= self.base, - "self.ptr = {:?} < {:?} = self.base", - *self.ptr.get(), - self.base - ); + #[inline] + pub fn used_bytes(&self) -> usize { + // TODO: safety: UnsafeCell.get() + // TODO: safety: prove that ∀Γ: reachable, Γ |- (ptr, base): same alloc + unsafe { (*self.ptr.get()).offset_from(self.base) as usize } + } - self.top.addr() - (*self.ptr.get()).addr() + #[inline(always)] + unsafe fn free_bytes(&self) -> usize { + self.capacity() - self.used_bytes() } pub unsafe fn alloc(&self, size: usize) -> *mut u8 { - let aligned_size = size.next_multiple_of(size); - if self.free_space() >= aligned_size { + self.debug_check_invariants(); + + let aligned_size = size.next_multiple_of(T::align()); + if self.free_bytes() >= aligned_size { + // TODO: make this an atomic add let ptr = *self.ptr.get(); *self.ptr.get() = ptr.add(aligned_size) as *mut _; ptr @@ -112,17 +137,41 @@ impl RawBlock { ptr::null_mut() } } + + /// Moves `ptr` back to `new_size`. + /// + /// Note that this method does *not* deallocate what was placed in the [`RawBlock`]. + pub fn shrink(&mut self, new_size: usize) { + self.debug_check_invariants(); + + assert!( + new_size <= self.used_bytes(), + "Shrink cannot grow: new_size = {:?} > allocated = {:?}", + new_size, + self.used_bytes() + ); + + // SAFETY: + // - Asserted: new_size <= self.capacity + // - Definition: self.base := alloc(self.capacity) + let new_ptr = unsafe { self.base.add(new_size) }; + + debug_assert!(new_ptr as usize <= (*self.ptr.get_mut()) as usize,); + + *self.ptr.get_mut() = new_ptr as *mut u8; + + self.debug_check_invariants(); + } } impl Drop for RawBlock { fn drop(&mut self) { if !self.base.is_null() { unsafe { - let layout = alloc::Layout::from_size_align_unchecked(self.size(), T::align()); + let layout = alloc::Layout::from_size_align_unchecked(self.capacity(), T::align()); alloc::dealloc(self.base as *mut _, layout); } - self.top = ptr::null(); self.base = ptr::null(); *self.ptr.get_mut() = ptr::null_mut(); }