popping off!

This commit is contained in:
nora 2021-12-21 14:02:15 +01:00
parent 338b7cc1ef
commit 1214d7245e
2 changed files with 96 additions and 8 deletions

View file

@ -23,7 +23,7 @@
//! │ ╰────────│──────────────────────────────────────╮
//! │ │ │
//! │ ╭────────╯ │
//! Heap ▼ ▼
//! Heap ▼ ▼ PtrData PtrData
//! ╭────────────┬─────────┬─────────────────┬──────────────┬──────────────╮
//! value │ "hello" │ "uwu" │ <uninit> │ 0 - 5 │ 5 - 3 │
//! ├────────────┼─────────┼─────────────────┼──────────────┼──────────────┤
@ -61,6 +61,7 @@ pub struct Vechonk<T: ?Sized> {
_marker: PhantomData<T>,
}
/// The offset + metadata for each element, stored at the end
struct PtrData<T: ?Sized> {
offset: usize,
meta: <T as Pointee>::Metadata,
@ -74,10 +75,12 @@ impl<T: ?Sized> Clone for PtrData<T> {
}
impl<T: ?Sized> Vechonk<T> {
/// 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<T: ?Sized> Vechonk<T> {
/// 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<T: ?Sized> Vechonk<T> {
}
}
/// Get the last element, returns `None` if the `Vechonk` is empty
pub fn pop(&mut self) -> Option<Box<T>> {
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::<T>(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<T: ?Sized> Vechonk<T> {
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<T> {
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<T>;
// 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<T>`
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) };

View file

@ -162,3 +162,33 @@ fn grow_alignment() {
let _ = chonk[0];
let _ = chonk[1];
}
#[test]
fn popping() {
let mut chonk = Vechonk::<str>::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")
}