pop, downcast, with_capacity

This commit is contained in:
missing 2022-05-13 13:14:18 -05:00 committed by missing
parent 2ce9385f85
commit 79c5e2fef3
2 changed files with 143 additions and 19 deletions

View file

@ -9,7 +9,7 @@
//!
//! # Examples
//!
//! You can create a `Vec` with [`Vec::new`]:
//! You can create a vector with [`Vec::new`]:
//!
//! ```
//! # use std::fmt::Debug;
@ -25,14 +25,18 @@
//! # use std::fmt::Debug;
//! let vec: Vec<i32> = vec![1, 2, 3];
//! // check the docs for `vec!` for more info on this syntax
//! let vec_boxed: Vec<dyn Debug> = vec![box: Box::new(1) as _, Box::new("foo") as _, Box::new(true) as _];
//! let vec_boxed: Vec<dyn Debug> = vec![box:
//! Box::new(1) as _,
//! Box::new("foo") as _,
//! Box::new(true) as _
//! ];
//! let vec_unsized: Vec<dyn Debug> = vec![unsized: 1, "foo", true];
//! # assert_eq!(format!("{:?}", vec), "[1, 2, 3]");
//! # assert_eq!(format!("{:?}", vec_boxed), r#"[1, "foo", true]"#);
//! # assert_eq!(format!("{:?}", vec_unsized), r#"[1, "foo", true]"#);
//! ```
//!
//! a `Vec` can be pushed to with [`Vec::push`]:
//! A vector can be pushed to with [`Vec::push`]:
//!
//! ```
//! # use dyn_vec::prelude::{*, vec};
@ -43,7 +47,7 @@
//! # assert_eq!(format!("{:?}", vec), "[1, 2, 3]");
//! ```
//!
//! ...and with [`push_box`] and [`push_unsize`]
//! ...and with [`push_box`] and [`push_unsize`]:
//!
//! ```
//! # use dyn_vec::prelude::{*, vec};
@ -82,6 +86,7 @@
//! └─ aligned to 8 also aligned to 8 ─┘
//! ```
//!
//! [`Vec<T: ?Sized>`]: Vec
//! [`push_box`]: Vec::push_box
//! [`push_unsize`]: Vec::push_unsize
@ -89,10 +94,16 @@
#![feature(layout_for_ptr)]
#![feature(coerce_unsized)]
#![warn(missing_docs)]
#[cfg(test)]
mod test;
// TODO: maybe remove this?
// Too small to put in its own file
/// Prelude, suitable for glob imports.
///
/// Using `prelude::*` will cause a conflict for the `vec!` macro. `prelude::{*, vec}` is recommended.
pub mod prelude {
pub use super::{Vec, vec};
}
@ -104,7 +115,8 @@ use std::{
alloc::{alloc, Layout, dealloc},
mem::{size_of, size_of_val, align_of_val, self, size_of_val_raw},
slice,
ops::{CoerceUnsized, Index, IndexMut}
ops::{CoerceUnsized, Index, IndexMut},
any::Any
};
/// Copy `size` bytes of memory from `src` to `dst`.
@ -147,7 +159,7 @@ pub use iter::*;
type Extra<T> = *const T;
impl<T: ?Sized> Vec<T> {
/// Creates a new, empty `Vec`.
/// Creates a new, empty vector.
pub fn new() -> Self {
let ptr = NonNull::dangling();
Self {
@ -159,9 +171,9 @@ impl<T: ?Sized> Vec<T> {
}
}
/// Creates a new `Vec` with the given capacity (measured in bytes).
pub fn with_capacity(cap: usize) -> Self {
let ptr = NonNull::new(unsafe { alloc(Layout::from_size_align(cap, 8).unwrap()) }).unwrap();
/// Creates a new vector with the given capacity, measured in bytes.
pub fn with_capacity_bytes(cap: usize) -> Self {
let ptr = NonNull::new(unsafe { alloc(Layout::from_size_align(cap, 8).unwrap().pad_to_align()) }).unwrap();
Self {
ptr,
len: 0,
@ -171,7 +183,12 @@ impl<T: ?Sized> Vec<T> {
}
}
/// Appends an element to the end of the `Vec`.
/// Creates a new vector with enough capacity to hold the given amount of `U`s.
pub fn with_capacity_for<U>(cap: usize) -> Self {
Self::with_capacity_bytes(cap * (size_of::<U>() + size_of::<Extra<U>>()))
}
/// Appends an element to the end of the vector.
///
/// Only avaliable if `T: Sized`.
pub fn push(&mut self, v: T) where T: Sized {
@ -179,7 +196,7 @@ impl<T: ?Sized> Vec<T> {
mem::forget(v);
}
/// Appends an (possibly unsized) boxed element to the end of the `Vec`.
/// Appends an (possibly unsized) boxed element to the end of the vector.
pub fn push_box(&mut self, v: Box<T>) {
let ptr = Box::into_raw(v);
unsafe {
@ -188,7 +205,7 @@ impl<T: ?Sized> Vec<T> {
}
}
/// Appends a sized element of type `U` to the end of the `Vec`, given that it can be `CoerceUnsized` to a `T`.
/// Appends a sized element of type `U` to the end of the vector, given that it can be `CoerceUnsized` to a `T`.
pub fn push_unsize<U>(&mut self, v: U) where for<'a> &'a U: CoerceUnsized<&'a T> {
let v_unsized: &T = &v;
unsafe { self.push_raw(v_unsized) };
@ -220,7 +237,7 @@ impl<T: ?Sized> Vec<T> {
align_up_mut(self.end_ptr.as_ptr(), align_of_val(v))
}
/// Checks if a given element will fill in the `Vec` without reallocations.
/// 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 needed_space = size_of_val(v) + size_of::<Extra<T>>();
@ -240,6 +257,17 @@ impl<T: ?Sized> Vec<T> {
self.len += 1;
}
/// Pops an element off the end of the vector, putting it in a [`Box`].
pub fn pop(&mut self) -> Option<Box<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))))
}
}
unsafe fn realloc(&mut self, size: usize) {
let layout = Layout::from_size_align_unchecked(size, 8).pad_to_align();
if self.capacity == 0 {
@ -303,6 +331,9 @@ impl<T: ?Sized> Vec<T> {
*self.get_ptr_to_extra(index + 1)
}
/// Gets a reference to the element at the specified index.
///
/// Returns `None` if the index is out-of-bounds.
pub fn get(&self, index: usize) -> Option<&T> {
if index < self.len {
Some(unsafe { self.get_unchecked(index) })
@ -311,10 +342,16 @@ impl<T: ?Sized> Vec<T> {
}
}
/// Gets a reference to the element at the specified index.
///
/// Immediate UB if the index is out-of-bounds.
pub unsafe fn get_unchecked(&self, index: usize) -> &T {
&*self.get_ptr(index)
}
/// Gets a mutable reference to the element at the specified index.
///
/// Returns `None` if the index is out-of-bounds.
pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
if index < self.len {
Some(unsafe { self.get_unchecked_mut(index) })
@ -323,11 +360,14 @@ impl<T: ?Sized> Vec<T> {
}
}
/// Gets a mutable reference to the element at the specified index.
///
/// Immediate UB if the index is out-of-bounds.
pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut T {
&mut *(self.get_ptr(index) as *mut _)
}
/// Returns the length of the `Vec`, which is how many items it contains.
/// Returns the length of the vector, which is how many items it contains.
pub fn len(&self) -> usize {
self.len
}
@ -337,22 +377,22 @@ impl<T: ?Sized> Vec<T> {
self.capacity
}
/// Returns a pointer to the allocation of the `Vec`.
/// Returns a pointer to the allocation of the vector.
pub fn as_ptr(&self) -> *const u8 {
self.ptr.as_ptr()
}
/// Returns a mutable pointer to the allocation of the `Vec`.
/// Returns a mutable pointer to the allocation of the vector.
pub fn as_mut_ptr(&mut self) -> *mut u8 {
self.ptr.as_ptr()
}
/// Iterates over the `Vec` by-ref.
/// Iterates over the vector by-ref.
pub fn iter(&self) -> Iter<T> {
Iter::new(self)
}
/// Iterates over the `Vec` by-mut.
/// Iterates over the vector by-mut.
pub fn iter_mut(&mut self) -> IterMut<T> {
IterMut::new(self)
}
@ -395,6 +435,9 @@ impl<T: ?Sized> Vec<T> {
}
impl<T> Vec<[T]> {
/// Returns a slice over all the elements in the vector.
///
/// Only avaliable for `Vec<[T]>`.
pub fn as_slice_flatten(&self) -> &[T] {
assert!(self.len > 0);
@ -408,6 +451,9 @@ impl<T> Vec<[T]> {
}
}
/// Returns a mutable slice over all the elements in the vector.
///
/// Only avaliable for `Vec<[T]>`.
pub fn as_mut_slice_flatten(&mut self) -> &mut [T] {
assert!(self.len > 0);
@ -422,6 +468,45 @@ impl<T> Vec<[T]> {
}
}
impl Vec<dyn Any> {
/// Gets a reference to the element at then specified index, downcasting it to the specified type.
///
/// Same as `.get().map(|v| v.downcast()).flatten()`.
pub fn downcast_get<T: Any>(&self, index: usize) -> Option<&T> {
self.get(index)?.downcast_ref()
}
/// Gets a mutable reference to the element at then specified index, downcasting it to the specified type.
///
/// Same as `.get_mut().map(|v| v.downcast_mut()).flatten()`.
pub fn downcast_get_mut<T: Any>(&mut self, index: usize) -> Option<&mut T> {
self.get_mut(index)?.downcast_mut()
}
/// Pops an element off the end of the vector, downcasting it to the specified type.
///
/// If the element is not of type `T`, the element will not be popped.
///
/// 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());
self.len -= 1;
v
}
}
}
impl<T> Vec<T> {
/// Creates a new vector that can hold the given amount of `T`s.
///
/// Only avaliable when `T: Sized`.
pub fn with_capacity(cap: usize) -> Self {
Self::with_capacity_for::<T>(cap)
}
}
impl<T: ?Sized> Drop for Vec<T> {
fn drop(&mut self) {
unsafe {

View file

@ -1,5 +1,7 @@
//! Always use `cargo miri test`, and never just `cargo test`.
use super::prelude::{*, vec};
use std::{fmt::Debug, sync::atomic::{AtomicBool, Ordering}};
use std::{fmt::Debug, sync::atomic::{AtomicBool, Ordering}, any::Any};
trait DebugExt: Debug {
fn debug(&self) -> String {
@ -138,4 +140,41 @@ fn zst() {
drop(el);
}
assert_eq!(vec.debug(), "[(), (), ()]");
}
#[test]
fn downcast() {
let mut vec: Vec<dyn Any> = vec![unsized: 1, String::from("foo"), true];
assert_eq!(vec.downcast_get::<f32>(0), None);
assert_eq!(vec.downcast_get::<i32>(0), Some(&1));
assert_eq!(vec.downcast_get_mut::<f32>(1), None);
assert_eq!(vec.downcast_get_mut::<String>(1), Some(&mut String::from("foo")));
assert_eq!(vec.downcast_pop::<f32>(), None);
assert_eq!(vec.downcast_pop::<bool>(), Some(true));
}
#[test]
fn pop() {
let mut vec: Vec<dyn Debug> = vec![unsized: 1, String::from("foo"), true];
assert_eq!(vec.pop().debug(), "Some(true)");
assert_eq!(vec.pop().debug(), "Some(\"foo\")");
assert_eq!(vec.pop().debug(), "Some(1)");
assert_eq!(vec.pop().debug(), "None");
}
#[test]
fn with_capacity() {
// 4 and not 3 cause of `Layout::pad_to_align`
let mut vec = Vec::with_capacity(4);
let prev_ptr = vec.as_ptr();
vec.push(1);
vec.push(2);
vec.push(3);
vec.push(4);
assert_eq!(prev_ptr, vec.as_ptr());
}