From 79c5e2fef3ca0a755ab8085343fa8c9300a56705 Mon Sep 17 00:00:00 2001 From: missing Date: Fri, 13 May 2022 13:14:18 -0500 Subject: [PATCH] pop, downcast, with_capacity --- src/lib.rs | 121 ++++++++++++++++++++++++++++++++++++++++++++-------- src/test.rs | 41 +++++++++++++++++- 2 files changed, 143 insertions(+), 19 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2b33ab9..59f2ca5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 = vec![1, 2, 3]; //! // check the docs for `vec!` for more info on this syntax -//! let vec_boxed: Vec = vec![box: Box::new(1) as _, Box::new("foo") as _, Box::new(true) as _]; +//! let vec_boxed: Vec = vec![box: +//! Box::new(1) as _, +//! Box::new("foo") as _, +//! Box::new(true) as _ +//! ]; //! let vec_unsized: Vec = 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`]: 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 = *const T; impl Vec { - /// Creates a new, empty `Vec`. + /// Creates a new, empty vector. pub fn new() -> Self { let ptr = NonNull::dangling(); Self { @@ -159,9 +171,9 @@ impl Vec { } } - /// 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 Vec { } } - /// 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(cap: usize) -> Self { + Self::with_capacity_bytes(cap * (size_of::() + size_of::>())) + } + + /// 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 Vec { 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) { let ptr = Box::into_raw(v); unsafe { @@ -188,7 +205,7 @@ impl Vec { } } - /// 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(&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 Vec { 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::>(); @@ -240,6 +257,17 @@ impl Vec { self.len += 1; } + /// Pops an element off the end of the vector, putting it in a [`Box`]. + pub fn pop(&mut self) -> Option> { + 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 Vec { *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 Vec { } } + /// 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 Vec { } } + /// 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 Vec { 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 { Iter::new(self) } - /// Iterates over the `Vec` by-mut. + /// Iterates over the vector by-mut. pub fn iter_mut(&mut self) -> IterMut { IterMut::new(self) } @@ -395,6 +435,9 @@ impl Vec { } impl 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 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 Vec<[T]> { } } +impl Vec { + /// 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(&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(&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(&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()); + self.len -= 1; + v + } + } +} + +impl Vec { + /// 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::(cap) + } +} + impl Drop for Vec { fn drop(&mut self) { unsafe { diff --git a/src/test.rs b/src/test.rs index 97f7926..0b5cc28 100644 --- a/src/test.rs +++ b/src/test.rs @@ -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 = vec![unsized: 1, String::from("foo"), true]; + + assert_eq!(vec.downcast_get::(0), None); + assert_eq!(vec.downcast_get::(0), Some(&1)); + assert_eq!(vec.downcast_get_mut::(1), None); + assert_eq!(vec.downcast_get_mut::(1), Some(&mut String::from("foo"))); + assert_eq!(vec.downcast_pop::(), None); + assert_eq!(vec.downcast_pop::(), Some(true)); +} + +#[test] +fn pop() { + let mut vec: Vec = 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()); } \ No newline at end of file