diff --git a/src/lib.rs b/src/lib.rs index b850698..cd47fa2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,7 +23,7 @@ //! │ ╰────────│──────────────────────────────────────╮ //! │ │ │ //! │ ╭────────╯ │ -//! Heap ▼ ▼ ▼ +//! Heap ▼ ▼ PtrData PtrData ▼ //! ╭────────────┬─────────┬─────────────────┬──────────────┬──────────────╮ //! value │ "hello" │ "uwu" │ │ 0 - 5 │ 5 - 3 │ //! ├────────────┼─────────┼─────────────────┼──────────────┼──────────────┤ @@ -61,6 +61,7 @@ pub struct Vechonk { _marker: PhantomData, } +/// The offset + metadata for each element, stored at the end struct PtrData { offset: usize, meta: ::Metadata, @@ -74,10 +75,12 @@ impl Clone for PtrData { } impl Vechonk { + /// The amount of elements in the `Vechonk`, O(1) pub const fn len(&self) -> usize { self.len } + /// Whether the `Vechonk` is empty, O(1) pub const fn is_empty(&self) -> bool { self.len() == 0 } @@ -85,7 +88,6 @@ impl Vechonk { /// Create a new empty Vechonk that doesn't allocate anything pub const fn new() -> Self { Self { - // SAFETY: 1 is not 0 ptr: NonNull::dangling(), len: 0, cap: 0, @@ -185,6 +187,54 @@ impl Vechonk { } } + /// Get the last element, returns `None` if the `Vechonk` is empty + pub fn pop(&mut self) -> Option> { + if self.is_empty() { + return None; + } + + // SAFETY: `self.len - 1` is the last element, and therefore not out of bounds + let data = unsafe { self.get_data(self.len - 1) }; + + // SAFETY: We can assume that the `offset` from `data` is not out of bounds + let elem_ptr = unsafe { self.ptr.as_ptr().add(data.offset) }; + + // allocate a new `Box` for the return value + let elem_fat_ptr = ptr::from_raw_parts_mut::(elem_ptr as *mut (), data.meta); + // SAFETY: The metadata has been preserved, and the pointer has been properly aligned and initialized + // when the element was added + let elem_fat_ref = unsafe { &*elem_fat_ptr }; + + let element_box_layout = Layout::for_value(elem_fat_ref); + + // SAFETY: TODO does not work with ZST + let box_ptr = unsafe { alloc::alloc::alloc(element_box_layout) }; + + if box_ptr.is_null() { + alloc::alloc::handle_alloc_error(element_box_layout); + } + + let elem_size = mem::size_of_val(elem_fat_ref); + // SAFETY: The new allocation doesn't overlap, `box_ptr` was just allocated and is non_null + // For `elem_ptr`, see safety comments above, the size was obtained above as well + unsafe { + ptr::copy_nonoverlapping(elem_ptr, box_ptr, elem_size); + } + + // SAFETY: See above for both variables. `data.meta` is the valid metadata for the element + let box_fat_ptr = ptr::from_raw_parts_mut(box_ptr as *mut (), data.meta); + + // We don't need to care about our memory, we can just decrement the `len` and let the old memory be, it's + // now semantically uninitialized + self.len -= 1; + + // SAFETY: We decremented the `len`, so no one else can get access to the element, + // therefore it's safe to transfer ownership to the Box here + let return_box = unsafe { Box::from_raw(box_fat_ptr) }; + + Some(return_box) + } + /// Get a reference to an element at the index. Returns `None` if the index is out of bounds pub fn get(&self, index: usize) -> Option<&T> { if index < self.len { @@ -297,19 +347,27 @@ impl Vechonk { self.cap = size.get(); } - /// Get a raw ptr to an element. Be careful about casting this into a `mut &T` - /// # SAFETY - /// The index must be in bounds - unsafe fn get_unchecked_ptr(&self, index: usize) -> *mut T { + /// Get the data for the index + /// # Safety + /// `index` must not be out of bounds + unsafe fn get_data(&self, index: usize) -> PtrData { let data_offset = self.offset_for_data(index); - // SAFETY: We can assume that the index is valid. + // SAFETY: The offset will always be less than `self.cap`, because we can't have more than `self.len` `PtrData` let data_ptr = unsafe { self.ptr.as_ptr().add(data_offset) }; let data_ptr = data_ptr as *mut PtrData; // SAFETY: The pointer is aligned because `self.ptr` is aligned and `data_offset` is a multiple of the alignment // The value behind it is always a `PtrData` - let data = unsafe { *data_ptr }; + unsafe { *data_ptr } + } + + /// Get a raw ptr to an element. Be careful about casting this into a `mut &T` + /// # SAFETY + /// The index must be in bounds + unsafe fn get_unchecked_ptr(&self, index: usize) -> *mut T { + // SAFETY: We can assume that `index` is valid + let data = unsafe { self.get_data(index) }; let elem_ptr = unsafe { self.ptr.as_ptr().add(data.offset) }; diff --git a/src/test.rs b/src/test.rs index 9448bb3..def5fbb 100644 --- a/src/test.rs +++ b/src/test.rs @@ -162,3 +162,33 @@ fn grow_alignment() { let _ = chonk[0]; let _ = chonk[1]; } + +#[test] +fn popping() { + let mut chonk = Vechonk::::with_capacity(512); + + chonk.push("hello".into()); + //chonk.push("uwu".into()); + //chonk.push("I'm popping off!".into()); + + //let popping = chonk.pop().unwrap(); + //let uwu = chonk.pop().unwrap(); + let hello = chonk.pop().unwrap(); + let end = chonk.pop(); + + //assert_eq!(popping.as_ref(), "hello"); + //assert_eq!(uwu.as_ref(), "uwu"); + assert_eq!(hello.as_ref(), "hello"); + assert_eq!(end, None); + + //drop(popping); + //drop(uwu); + drop(hello); + drop(chonk); +} + +#[test] +#[ignore] +fn zst() { + todo!("handle them") +}