diff --git a/src/lib.rs b/src/lib.rs index a86ebff..2b33ab9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,90 @@ +//! # A [`Vec`] +//! +//! A dynamic length collection of unsized elements, akin to [`std::vec::Vec`]. +//! +//! This crate depends on a number of unstable features, namely: +//! - `#![feature(ptr_metadata)]`, to allow manipulating the metadata of pointers. +//! - `#![feature(layout_for_ptr)]`, to allow getting the size/alignment of a value with only a pointer to it. +//! - `#![feature(coerce_unsized)]`, to add trait bounds for types which can be unsized to another type. +//! +//! # Examples +//! +//! You can create a `Vec` with [`Vec::new`]: +//! +//! ``` +//! # use std::fmt::Debug; +//! # use dyn_vec::prelude::*; +//! let vec: Vec = Vec::new(); +//! # assert_eq!(format!("{:?}", vec), "[]"); +//! ``` +//! +//! or with the [`vec!`] macro: +//! +//! ``` +//! # use dyn_vec::prelude::{*, vec}; +//! # 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_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`]: +//! +//! ``` +//! # use dyn_vec::prelude::{*, vec}; +//! let mut vec: Vec = vec![]; +//! vec.push(1); +//! vec.push(2); +//! vec.push(3); +//! # assert_eq!(format!("{:?}", vec), "[1, 2, 3]"); +//! ``` +//! +//! ...and with [`push_box`] and [`push_unsize`] +//! +//! ``` +//! # use dyn_vec::prelude::{*, vec}; +//! # use std::fmt::Debug; +//! let mut vec: Vec = vec![]; +//! vec.push_box(Box::new(1)); +//! vec.push_box(Box::new("foo")); +//! vec.push_box(Box::new(true)); +//! +//! vec.push_unsize(2); +//! vec.push_unsize("bar"); +//! vec.push_unsize(false); +//! # assert_eq!(format!("{:?}", vec), r#"[1, "foo", true, 2, "bar", false]"#); +//! ``` +//! +//! # Data Layout +//! +//! ```text +//! Vec +//! ┌────┬────┬────┬────┐ +//! │ptr │len │cap │end │ +//! └─┬──┴────┴─┬──┴─┬──┘ +//! │ │ │ +//! │ └────┼───────────────────────────────────────────────┐ +//! ┌─┘ └───────────────────┐ │ +//! │ │ │ +//! ▼ ▼ ▼ +//! ┌────┬────┬─────┬──────────┬───┬─────┬───────────────┬───┬───┬───┐ +//! │pad │elem│pad │elem │pad│elem │ │ptr│ptr│ptr│ +//! └────┴────┴─────┴──────────┴───┴─────┴───────────────┴─┬─┴─┬─┴─┬─┘ +//! ▲ ▲ ▲ ▲ │ │ │ ▲ +//! │ │ │ └───────────────────────┘ │ │ │ +//! │ │ └──────────────────────────────────────────┘ │ │ +//! │ └─────────────────────────────────────────────────────────┘ │ +//! │ │ +//! └─ aligned to 8 also aligned to 8 ─┘ +//! ``` +//! +//! [`push_box`]: Vec::push_box +//! [`push_unsize`]: Vec::push_unsize + #![feature(ptr_metadata)] #![feature(layout_for_ptr)] #![feature(coerce_unsized)] @@ -5,30 +92,29 @@ #[cfg(test)] mod test; -pub mod prelude; +// Too small to put in its own file +pub mod prelude { + pub use super::{Vec, vec}; +} use core::panic; use std::{ ptr::{NonNull, self, drop_in_place, metadata}, marker::PhantomData, - alloc::{alloc, Layout}, + alloc::{alloc, Layout, dealloc}, mem::{size_of, size_of_val, align_of_val, self, size_of_val_raw}, slice, ops::{CoerceUnsized, Index, IndexMut} }; -unsafe fn dealloc(ptr: *mut u8, layout: Layout) { - std::alloc::dealloc(ptr, layout) -} - /// 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_from` until the code was mostly complete. +// 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) { - dst.copy_from(src, size); + src.copy_to(dst, size); } fn align_up(ptr: *const T, align: usize) -> *const T { @@ -41,9 +127,9 @@ fn align_up_mut(ptr: *mut T, align: usize) -> *mut T { align_up(ptr as _, align) as _ } -/// A heap allocated, dynamically sized collection of `?Sized` elements. +/// A heap allocated, dynamic length collection of `?Sized` elements. /// -/// See [`::alloc::vec::Vec`] (the standard library `Vec` type) for more information. +/// See [`std::vec::Vec`] (the standard library `Vec` type) for more information. pub struct Vec { ptr: NonNull, len: usize, @@ -300,6 +386,12 @@ impl Vec { mem::forget(self); new_vec } + + unsafe fn dealloc(&self) { + if self.capacity != 0 { + dealloc(self.ptr.as_ptr(), Layout::from_size_align_unchecked(self.capacity, 8)); + } + } } impl Vec<[T]> { @@ -337,7 +429,7 @@ impl Drop for Vec { println!("dropping {}", i); drop_in_place(self.get_unchecked_mut(i)); } - dealloc(self.ptr.as_ptr(), Layout::from_size_align_unchecked(self.capacity, 8)); + self.dealloc(); } } } diff --git a/src/prelude.rs b/src/prelude.rs deleted file mode 100644 index d6eaf78..0000000 --- a/src/prelude.rs +++ /dev/null @@ -1 +0,0 @@ -pub use super::{Vec, vec}; \ No newline at end of file diff --git a/src/test.rs b/src/test.rs index 6841f91..97f7926 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,4 +1,4 @@ -use super::prelude::{Vec, vec}; +use super::prelude::{*, vec}; use std::{fmt::Debug, sync::atomic::{AtomicBool, Ordering}}; trait DebugExt: Debug { @@ -47,7 +47,6 @@ fn all_macro() { Box::new(true) as _, ]; let vec3: Vec = vec![unsized: 1, String::from("foo"), true]; - // assert_eq!(vec2, vec3); // doesnt compile, but would theoretically work assert_eq!(vec2.debug(), vec3.debug()); }