Vec::remove and some refactors

This commit is contained in:
missing 2022-05-17 09:47:54 -05:00 committed by missing
parent b42bee180e
commit f1bb8f9f5b
4 changed files with 145 additions and 90 deletions

View file

@ -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<T: ?Sized> Default for Vec<T> {
fn default() -> Self {
@ -70,4 +70,49 @@ impl<T: ?Sized, U: ?Sized> Extend<Box<U>> for Vec<T> where Box<U>: CoerceUnsized
self.push_box(item);
}
}
}
impl<T> Deref for Vec<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
unsafe { slice::from_raw_parts(self.get_ptr(0), self.len) }
}
}
impl<T> DerefMut for Vec<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { slice::from_raw_parts_mut(self.get_ptr(0) as _, self.len) }
}
}
unsafe impl<T: Send> Send for Vec<T> {}
unsafe impl<T: Sync> Sync for Vec<T> {}
impl<T> From<StdVec<T>> for Vec<T> {
fn from(std_vec: StdVec<T>) -> Self {
let mut vec = Vec::new();
let new_cap = (size_of::<T>() + size_of::<Extra<T>>()) * std_vec.len();
unsafe { vec.realloc(new_cap); }
for item in std_vec {
unsafe { vec.push_raw_unchecked(&item) }
}
vec
}
}
impl<T> From<Vec<T>> for StdVec<T> {
fn from(mut vec: Vec<T>) -> 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
}
}

View file

@ -71,13 +71,13 @@ impl<'a, T: ?Sized> Iterator for Iter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
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<Self::Item> {
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<Self::Item> {
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<Self::Item> {
unsafe { self.base.next_back().map(|v| &mut *v) }
self.base.next_back().map(|v| unsafe { &mut *v })
}
}
@ -158,23 +158,13 @@ impl<T: ?Sized> Iterator for IntoIter<T> {
type Item = Box<T>;
fn next(&mut self) -> Option<Self::Item> {
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<T: ?Sized> DoubleEndedIterator for IntoIter<T> {
fn next_back(&mut self) -> Option<Self::Item> {
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() })
}
}

View file

@ -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<T: ?Sized>(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<T: ?Sized>(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<T: ?Sized> Vec<T> {
}
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::<Extra<T>>();
let new_alloc_size = self.capacity * 2 + size_of_val_raw(v) * 2 + size_of::<Extra<T>>();
self.realloc(new_alloc_size);
}
@ -265,26 +244,22 @@ impl<T: ?Sized> Vec<T> {
/// 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::<Extra<T>>();
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::<T>(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<T: ?Sized> Vec<T> {
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<T: ?Sized> Vec<T> {
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::<Extra<T>>();
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<T: ?Sized> Vec<T> {
/// 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<T> {
self.ptr.as_ptr()
.wrapping_add(self.capacity)
.add_bytes(self.capacity)
.cast::<Extra<T>>()
.wrapping_sub(index)
}
@ -362,6 +332,10 @@ impl<T: ?Sized> Vec<T> {
*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<T: ?Sized> Vec<T> {
}
/// 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<T: ?Sized> Vec<T> {
/// Converts a `Vec<T: Sized>` into a `Vec<U: ?Sized>`, given that `T` can be `CoerceUnsized` into `U`.
pub fn unsize<U: ?Sized>(mut self) -> Vec<U> where for<'a> &'a T: CoerceUnsized<&'a U> {
if size_of::<Extra<U>>() > size_of::<Extra<T>>() {
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::<Extra<U>>();
let needed_size = elem_size + extra_size;
if needed_size > self.capacity {
@ -460,6 +436,7 @@ impl<T: ?Sized> Vec<T> {
// 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<T: ?Sized> Vec<T> {
// 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<T: ?Sized> Vec<T> {
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<Box<T>> 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<T> Vec<[T]> {
@ -503,9 +518,9 @@ impl<T> 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::<T>(), 0);
(end - start) / size_of::<T>() // integer division!
})
@ -522,9 +537,9 @@ impl<T> 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::<T>(), 0);
(end - start) / size_of::<T>() // integer division!
})
@ -554,8 +569,8 @@ impl Vec<dyn Any> {
/// Similiar to `.pop().map(|v| v.downcast()).flatten()`, but without an intermediate allocation.
pub fn downcast_pop<T: Any>(&mut self) -> Option<T> {
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
}

View file

@ -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<dyn Debug> = vec.unsize();
vec.push_unsize(String::from("foo"));
assert_eq!(vec.debug(), "[1, 2, 3, \"foo\"]");
}
#[test]
fn remove() {
let mut vec: Vec<dyn Debug> = vec![unsized: 1, String::from("foo"), true];
assert_eq!(vec.remove(1).unwrap().debug(), "\"foo\"");
assert_eq!(vec.debug(), "[1, true]");
}