diff --git a/src/impls.rs b/src/impls.rs index 1a171c2..271aa1b 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -3,7 +3,7 @@ #[allow(clippy::wildcard_imports)] use super::*; -use std::fmt::Debug; +use std::{fmt::Debug, ops::{Deref, DerefMut}, vec::Vec as StdVec}; impl Default for Vec { fn default() -> Self { @@ -70,4 +70,49 @@ impl Extend> for Vec where Box: CoerceUnsized self.push_box(item); } } +} + +impl Deref for Vec { + type Target = [T]; + + fn deref(&self) -> &Self::Target { + unsafe { slice::from_raw_parts(self.get_ptr(0), self.len) } + } +} + +impl DerefMut for Vec { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { slice::from_raw_parts_mut(self.get_ptr(0) as _, self.len) } + } +} + +unsafe impl Send for Vec {} +unsafe impl Sync for Vec {} + +impl From> for Vec { + fn from(std_vec: StdVec) -> Self { + let mut vec = Vec::new(); + let new_cap = (size_of::() + size_of::>()) * std_vec.len(); + unsafe { vec.realloc(new_cap); } + + for item in std_vec { + unsafe { vec.push_raw_unchecked(&item) } + } + + vec + } +} + +impl From> for StdVec { + fn from(mut vec: Vec) -> Self { + let mut std_vec = StdVec::with_capacity(vec.len); + + for item in vec.iter() { + std_vec.push(unsafe { (item as *const T).read() }); + } + + vec.len = 0; + + std_vec + } } \ No newline at end of file diff --git a/src/iter.rs b/src/iter.rs index 01c89b4..29ea5ff 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -71,13 +71,13 @@ impl<'a, T: ?Sized> Iterator for Iter<'a, T> { type Item = &'a T; fn next(&mut self) -> Option { - unsafe { self.base.next().map(|v| &*v) } + self.base.next().map(|v| unsafe { &*v }) } } impl<'a, T: ?Sized> DoubleEndedIterator for Iter<'a, T> { fn next_back(&mut self) -> Option { - unsafe { self.base.next_back().map(|v| &*v) } + self.base.next_back().map(|v| unsafe { &*v }) } } @@ -111,13 +111,13 @@ impl<'a, T: ?Sized> Iterator for IterMut<'a, T> { type Item = &'a mut T; fn next(&mut self) -> Option { - unsafe { self.base.next().map(|v| &mut *v) } + self.base.next().map(|v| unsafe { &mut *v }) } } impl<'a, T: ?Sized> DoubleEndedIterator for IterMut<'a, T> { fn next_back(&mut self) -> Option { - unsafe { self.base.next_back().map(|v| &mut *v) } + self.base.next_back().map(|v| unsafe { &mut *v }) } } @@ -158,23 +158,13 @@ impl Iterator for IntoIter { type Item = Box; fn next(&mut self) -> Option { - let ptr = self.base.next()?; - unsafe { - let alloc = alloc(Layout::for_value_raw(ptr)); - memcpy(ptr.cast(), alloc, size_of_val_raw(ptr)); - Some(Box::from_raw(ptr::from_raw_parts_mut(alloc.cast(), metadata(ptr)))) - } + Some(unsafe { self.base.next()?.read_to_box() }) } } impl DoubleEndedIterator for IntoIter { fn next_back(&mut self) -> Option { - let ptr = self.base.next_back()?; - unsafe { - let alloc = alloc(Layout::for_value_raw(ptr)); - memcpy(ptr.cast(), alloc, size_of_val_raw(ptr)); - Some(Box::from_raw(ptr::from_raw_parts_mut(alloc.cast(), metadata(ptr)))) - } + Some(unsafe { self.base.next_back()?.read_to_box() }) } } diff --git a/src/lib.rs b/src/lib.rs index 806d312..fa64be6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -107,6 +107,9 @@ #![feature(ptr_metadata)] #![feature(layout_for_ptr)] #![feature(coerce_unsized)] +// for `mod bad_things`, still a wip +// #![allow(incomplete_features)] +// #![feature(specialization)] #![warn(missing_docs)] #![warn(clippy::pedantic)] @@ -115,8 +118,7 @@ #[cfg(test)] mod test; -// TODO: maybe remove this? -// Too small to put in its own file +// TODO: maybe remove this? Its not that many imports /// Prelude, suitable for glob imports. /// /// Using `prelude::*` will cause a conflict for the `vec!` macro. `prelude::{*, vec}` is recommended. @@ -126,34 +128,18 @@ pub mod prelude { use core::panic; use std::{ - ptr::{NonNull, self, drop_in_place, metadata}, + ptr::{NonNull, drop_in_place}, marker::PhantomData, - alloc::{alloc, Layout, dealloc}, - mem::{size_of, size_of_val, align_of_val, self, size_of_val_raw}, + alloc::{Layout, alloc, dealloc}, + mem::{self, size_of, size_of_val, align_of_val, size_of_val_raw, align_of_val_raw}, slice, ops::{CoerceUnsized, Index, IndexMut}, any::Any }; -/// Copy `size` bytes of memory from `src` to `dst`. -/// -/// # Safety -/// -/// `src` must be valid for reads, `dst` must be valid for writes, etc, you get the idea. -// TODO: inline me! i didnt realize it was avaliable as `copy_to` until the code was mostly complete. -unsafe fn memcpy(src: *const u8, dst: *mut u8, size: usize) { - src.copy_to(dst, size); -} - -fn align_up(ptr: *const T, align: usize) -> *const T { - let (mut data, meta) = ptr.to_raw_parts(); - data = ((data as usize + align - 1) & !(align - 1)) as _; - ptr::from_raw_parts(data, meta) -} - -fn align_up_mut(ptr: *mut T, align: usize) -> *mut T { - align_up(ptr as _, align) as _ -} +mod ptr_ext; +use ptr_ext::*; +mod bad_things; /// A heap allocated, dynamic length collection of `?Sized` elements. /// @@ -244,15 +230,8 @@ impl Vec { } unsafe fn push_raw(&mut self, v: *const T) { - let size = size_of_val(&*v); - if !self.will_fit(&*v) { - // oh no! allocation too small! - - // make sure we have enough space for a new element, but also space for future elements - // this bit is tricky, we must make sure we have enough space for padding too, so its probably UB somehow - // FIXME: ^^^ - let new_alloc_size = self.capacity * 2 + size * 2 + size_of::>(); + let new_alloc_size = self.capacity * 2 + size_of_val_raw(v) * 2 + size_of::>(); self.realloc(new_alloc_size); } @@ -265,26 +244,22 @@ impl Vec { /// To check for this, call `will_fit`. /// In addition, the extra data for the element must be set using `set_extra_from_ptr`. fn get_next_elem_ptr(&self, v: &T) -> *mut u8 { - align_up_mut(self.end_ptr.as_ptr(), align_of_val(v)) + self.end_ptr.as_ptr().align_up(align_of_val(v)) } /// Checks if a given element will fit in the vector without reallocations. pub fn will_fit(&self, v: &T) -> bool { - let remaining_space = self.get_ptr_to_extra(self.len) as usize - self.end_ptr.as_ptr() as usize; + let remaining_space = self.get_ptr_to_extra(self.len).addr() - self.end_ptr.as_ptr().addr(); let needed_space = size_of_val(v) + size_of::>(); remaining_space >= needed_space } unsafe fn push_raw_unchecked(&mut self, v: *const T) { - let size = size_of_val(&*v); - let dest = self.get_next_elem_ptr(&*v); // this is mentioned by the `// SAFETY:` in `as_slice_flatten` + let dest = self.get_next_elem_ptr(&*v).with_meta_from(v); + v.copy_val_to(dest); + self.set_extra_from_ptr(self.len, dest); - memcpy(v.cast(), dest, size); - - let new_ptr = ptr::from_raw_parts::(dest.cast(), metadata(v)); - self.set_extra_from_ptr(self.len, new_ptr); - - self.end_ptr = NonNull::new_unchecked(dest.wrapping_add(size)); + self.end_ptr = NonNull::new_unchecked(dest.get_end().cast()); self.len += 1; } @@ -293,9 +268,7 @@ impl Vec { unsafe { self.len = self.len.checked_sub(1)?; let el = self.get_ptr(self.len); - let alloc = alloc(Layout::for_value_raw(el)); - memcpy(el.cast(), alloc, size_of_val_raw(el)); - Some(Box::from_raw(ptr::from_raw_parts_mut(alloc.cast(), metadata(el)))) + Some(el.read_to_box()) } } @@ -307,34 +280,31 @@ impl Vec { self.end_ptr = self.ptr; } else { - // cannot use realloc here + // cannot use mem::realloc here let new_alloc = NonNull::new(alloc(layout)).unwrap(); // data let mut ptr = new_alloc.as_ptr(); for i in 0..self.len { - let v = self.get_unchecked(i); + let v = self.get_ptr(i); - let size = size_of_val(v); - ptr = align_up_mut(ptr, align_of_val(v)); - memcpy((v as *const T).cast(), ptr, size); - let meta = self.get_ptr(i).to_raw_parts().1; - self.set_extra_from_ptr(i, ptr::from_raw_parts(ptr.cast(), meta)); - ptr = ptr.wrapping_add(size); + ptr = ptr.align_up(align_of_val_raw(v)); + v.copy_val_to(ptr); + self.set_extra_from_ptr(i, ptr.with_meta_from(v)); + ptr = ptr.wrapping_add(size_of_val_raw(v)); } self.end_ptr = NonNull::new_unchecked(ptr); - // metadata - let meta_src = self.get_ptr_to_extra(self.len); - let meta_dst = { + // extra + let extra_src = self.get_ptr_to_extra(self.len); + let extra_dst = { let current_alloc_end = self.ptr.as_ptr().wrapping_add(self.capacity); let new_alloc_end = new_alloc.as_ptr().wrapping_add(layout.size()); - let meta_len = current_alloc_end as usize - meta_src as usize; - new_alloc_end.wrapping_sub(meta_len) + let extra_len = current_alloc_end.addr() - extra_src.addr(); + new_alloc_end.wrapping_sub(extra_len) }; - let meta_size = self.len * size_of::>(); - memcpy(meta_src.cast(), meta_dst, meta_size); + extra_src.copy_to(extra_dst.cast(), self.len); dealloc(self.ptr.as_ptr(), Layout::from_size_align_unchecked(self.capacity, 8)); @@ -346,10 +316,10 @@ impl Vec { /// for internal use /// - /// NOTE: 1-indexed, to allow getting a pointer to the end of the alloc easily + /// # Note: 1-indexed, to allow getting a pointer to the end of the alloc easily fn get_ptr_to_extra(&self, index: usize) -> *mut Extra { self.ptr.as_ptr() - .wrapping_add(self.capacity) + .add_bytes(self.capacity) .cast::>() .wrapping_sub(index) } @@ -362,6 +332,10 @@ impl Vec { *self.get_ptr_to_extra(index + 1) } + unsafe fn get_ptr_before_pad(&self, index: usize) -> *const T { + self.get_ptr(index).with_addr_from(if index > 0 { self.get_ptr(index - 1).get_end().cast() } else { self.ptr.as_ptr() }) + } + /// Gets a reference to the element at the specified index. /// /// Returns `None` if the index is out-of-bounds. @@ -413,6 +387,8 @@ impl Vec { } /// Returns the capacity, which is the size of the allocation in bytes. + /// + /// Note the distinction from [`std::vec::Vec`], which returns how many elements it can hold. pub fn capacity(&self) -> usize { self.capacity } @@ -440,7 +416,7 @@ impl Vec { /// Converts a `Vec` into a `Vec`, given that `T` can be `CoerceUnsized` into `U`. pub fn unsize(mut self) -> Vec where for<'a> &'a T: CoerceUnsized<&'a U> { if size_of::>() > size_of::>() { - let elem_size = self.end_ptr.as_ptr() as usize - self.ptr.as_ptr() as usize; + let elem_size = self.end_ptr.as_ptr().addr() - self.ptr.as_ptr().addr(); let extra_size = self.len * size_of::>(); let needed_size = elem_size + extra_size; if needed_size > self.capacity { @@ -460,6 +436,7 @@ impl Vec { // new extra larger than old extra, must go from back to front for i in (0..self.len).rev() { + // using references here is necessary for unsizing coercion to work let current = unsafe { &*self.get_ptr(i) }; unsafe { new_vec.set_extra_from_ptr(i, current as &U) } } @@ -467,6 +444,7 @@ impl Vec { // new extra smaller or same size as old extra, must go from front to back for i in 0..self.len { + // using references here is necessary for unsizing coercion to work let current = unsafe { &*self.get_ptr(i) }; unsafe { new_vec.set_extra_from_ptr(i, current as &U) } } @@ -490,6 +468,43 @@ impl Vec { self.push_unsize(item); } } + + /// Removes the element at the specified index, shifting other elements over to fill the gap. + pub fn remove(&mut self, index: usize) -> Option> where T: std::fmt::Debug { + if index >= self.len { + return None + } + + if index == self.len - 1 { + return self.pop() + } + + unsafe { + let res = Some(self.get_ptr(index).read_to_box()); + + // starting from the now-empty spot, up to but not including the end... + for index in index..self.len-1 { + // get a pointer to the end of the previous element + let mut new_ptr = self.get_ptr_before_pad(index); + // align it up to the align of the NEXT element + let next_ptr = self.get_ptr(index + 1); + new_ptr = new_ptr.align_up(align_of_val_raw(next_ptr)); + + // if its the same, we can break as the rest will be useless + if new_ptr == next_ptr { break } + + // data + next_ptr.copy_val_to(new_ptr as *mut T); + + // extra + self.set_extra_from_ptr(index, new_ptr.with_meta_from(next_ptr)); + } + + self.len -= 1; + + res + } + } } impl Vec<[T]> { @@ -503,9 +518,9 @@ impl Vec<[T]> { // SAFETY: the slices should be contiguous by the logic of `push_raw_unchecked` unsafe { - slice::from_raw_parts(self.get_ptr(0).to_raw_parts().0.cast(), { - let start = self.get_ptr(0).to_raw_parts().0 as usize; - let end = self.end_ptr.as_ptr() as usize; + slice::from_raw_parts(self.get_ptr(0).data_ptr().cast(), { + let start = self.get_ptr(0).addr(); + let end = self.end_ptr.as_ptr().addr(); debug_assert_eq!((end - start) % size_of::(), 0); (end - start) / size_of::() // integer division! }) @@ -522,9 +537,9 @@ impl Vec<[T]> { // SAFETY: the slices should be contiguous by the logic of `push_raw_unchecked` unsafe { - slice::from_raw_parts_mut(self.get_ptr(0).to_raw_parts().0 as _, { - let start = self.get_ptr(0).to_raw_parts().0 as usize; - let end = self.end_ptr.as_ptr() as usize; + slice::from_raw_parts_mut(self.get_ptr(0).data_ptr() as _, { + let start = self.get_ptr(0).addr(); + let end = self.end_ptr.as_ptr().addr(); debug_assert_eq!((end - start) % size_of::(), 0); (end - start) / size_of::() // integer division! }) @@ -554,8 +569,8 @@ impl Vec { /// Similiar to `.pop().map(|v| v.downcast()).flatten()`, but without an intermediate allocation. pub fn downcast_pop(&mut self) -> Option { unsafe { - let el = self.get_ptr(self.len.checked_sub(1)?) as *mut dyn Any; - let v = Some(((&mut *el).downcast_mut()? as *mut T).read()); + let el = self.get_unchecked_mut(self.len.checked_sub(1)?); + let v = Some((el.downcast_mut()? as *mut T).read()); self.len -= 1; v } diff --git a/src/test.rs b/src/test.rs index 2ab619e..a46acb9 100644 --- a/src/test.rs +++ b/src/test.rs @@ -182,8 +182,6 @@ fn with_capacity() { assert_eq!(prev_ptr, vec.as_ptr()); vec.push(5); - vec.push(6); - vec.push(7); // should have realloc'ed by now assert_ne!(prev_ptr, vec.as_ptr()); @@ -195,4 +193,11 @@ fn unsize() { let mut vec: Vec = vec.unsize(); vec.push_unsize(String::from("foo")); assert_eq!(vec.debug(), "[1, 2, 3, \"foo\"]"); +} + +#[test] +fn remove() { + let mut vec: Vec = vec![unsized: 1, String::from("foo"), true]; + assert_eq!(vec.remove(1).unwrap().debug(), "\"foo\""); + assert_eq!(vec.debug(), "[1, true]"); } \ No newline at end of file