diff --git a/src/iter.rs b/src/iter.rs index 7ab57e8..365c86c 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -1,7 +1,7 @@ use crate::{RawVechonk, Vechonk}; use alloc::boxed::Box; use core::marker::PhantomData; -use core::mem; +use core::mem::ManuallyDrop; /// An iterator over the elements of a [`Vechonk`] pub struct Iter<'a, T: ?Sized> { @@ -105,18 +105,21 @@ pub struct IntoIter { } impl<'a, T: ?Sized> IntoIter { - pub(super) fn new(chonk: Vechonk) -> IntoIter { - let raw = chonk.raw.copy(); - - // We don't want to free the memory! - mem::forget(chonk); - + pub(crate) fn from_raw(raw: RawVechonk) -> Self { Self { raw, current_index: 0, _marker: PhantomData, } } + + pub(crate) fn new(chonk: Vechonk) -> IntoIter { + // We don't want to free the memory yet! + let chonk = ManuallyDrop::new(chonk); + let raw = chonk.raw.copy(); + + Self::from_raw(raw) + } } impl Iterator for IntoIter { diff --git a/src/lib.rs b/src/lib.rs index b6163b8..40cb33b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -94,6 +94,7 @@ impl Vechonk { self.raw.pop() } + #[cfg(any())] pub fn insert(&mut self, _index: usize, _element: Box) { todo!() } @@ -103,6 +104,7 @@ impl Vechonk { /// Even worse, after all the copying, it might realloc anyways because it couldn't fit in the space. /// /// Returns the old element at that + #[cfg(any())] pub fn replace(&mut self, _index: usize, _element: Box) -> Box { todo!() } @@ -163,17 +165,6 @@ impl Vechonk { // The pointer is aligned, because it has been aligned manually in `Self::push` unsafe { &*self.raw.get_unchecked_ptr(index) } } - - /// used for debugging memory layout - /// safety: cap must be 96 - #[allow(dead_code)] - #[doc(hidden)] - #[cfg(debug_assertions)] - pub unsafe fn debug_chonk(&self) { - let array = unsafe { *(self.raw.ptr.as_ptr() as *mut [u8; 96]) }; - - panic!("{:?}", array) - } } impl Index for Vechonk { diff --git a/src/raw.rs b/src/raw.rs index d087092..cbdefe7 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -65,10 +65,7 @@ impl RawVechonk { return vechonk; } - // SAFETY: capacity has been checked to not be 0 and the len is 0 - unsafe { - vechonk.realloc(NonZeroUsize::new_unchecked(capacity)); - } + vechonk.reset_alloc(NonZeroUsize::new(capacity).unwrap()); vechonk } @@ -290,71 +287,26 @@ impl RawVechonk { } fn regrow(&mut self, min_size: NonZeroUsize) { - // new_cap must be properly "aligned" for `PtrData` + // We just create a new one and copy all elements over. + // This is because it's almost impossible to copy around the alignment properly, + // as we need to dynamically align each element, and the alignment of our allocation + // might have decreased after the realloc. + let new_cap = force_align(min_size.get() * 2, Self::data_align()); - let old_ptr = self.ptr.as_ptr(); - let old_cap = self.cap; - - let last_data_index = self.len.saturating_sub(1); - let old_data_offset = self.offset_for_data(last_data_index); - - // SAFETY: `min_size` was already non-zero - // We will copy the elements over - unsafe { - self.realloc(NonZeroUsize::new_unchecked(new_cap)); - } - - // copy the elements first - // SAFETY: both pointers point to the start of allocations smaller than `self.elem_size` and own them - unsafe { - ptr::copy_nonoverlapping::(old_ptr, self.ptr.as_ptr(), self.elem_size); - } - - // then copy the data - // SAFETY: both pointers have been offset by less than `self.cap`, and the `data_section_size` fills the allocation perfectly - unsafe { - let new_data_ptr = self.ptr.as_ptr().add(self.offset_for_data(last_data_index)); - - ptr::copy_nonoverlapping::( - old_ptr.add(old_data_offset), - new_data_ptr, - self.data_section_size(), - ) - } - - // now free the old data - // SAFETY: This was previously allocated and is not used anymore - unsafe { - Self::dealloc(old_cap, old_ptr); - } + let new = RawVechonk::with_capacity(new_cap); + let old = mem::replace(self, new); + crate::IntoIter::from_raw(old).for_each(|e| { + self.push(e); + }); } - /// Reallocs the `Vechonk`, setting its capacity to `size`. This will not copy any elements. This will put the `Vechonk` - /// into an invalid state, since the `len` is still the length of the elements in the old allocation. - /// - /// This doesn't free any memory - /// - /// # Safety - /// The caller must either set the `len` to zero, or copy the elements to the new allocation by saving - /// `self.ptr` before calling this function. - unsafe fn realloc(&mut self, size: NonZeroUsize) { - // TODO this is *not* sound, since the alignment of some big elements might be wrong now - + /// Allocates the `Vechonk`, setting its capacity to `size`. + fn reset_alloc(&mut self, size: NonZeroUsize) { let layout = Layout::from_size_align(size.get(), Self::data_align()).unwrap(); // SAFETY: layout is guaranteed to have a non-zero size - let alloced_ptr; - - // we only care about it being zeroed for debugging since it makes it easier - #[cfg(debug_assertions)] - unsafe { - alloced_ptr = alloc::alloc::alloc_zeroed(layout) - } - #[cfg(not(debug_assertions))] - unsafe { - alloced_ptr = alloc::alloc::alloc(layout) - } + let alloced_ptr = unsafe { alloc::alloc::alloc(layout) }; self.ptr = NonNull::new(alloced_ptr).unwrap_or_else(|| alloc::alloc::handle_alloc_error(layout));