diff --git a/Cargo.toml b/Cargo.toml index c6c6ec1..35e7150 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] + +[features] +unstable = [] \ No newline at end of file diff --git a/README.md b/README.md index 12f7054..b022497 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,5 @@ -# dyn_vec +# A `Vec` +A dynamic length collection of unsized elements, akin to `std::vec::Vec`. + +Check the crate-level documentation for more info. \ No newline at end of file diff --git a/src/impls.rs b/src/impls.rs index 271aa1b..9952e1c 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -63,6 +63,17 @@ impl, U, const N: usize> PartialEq> for [U; N] { } } +#[cfg(not(feature = "unstable"))] +impl Extend> for Vec { + fn extend>>(&mut self, iter: I) { + for item in iter { + // TODO: optmize + self.push_box(item); + } + } +} + +#[cfg(feature = "unstable")] impl Extend> for Vec where Box: CoerceUnsized> { fn extend>>(&mut self, iter: I) { for item in iter { diff --git a/src/iter.rs b/src/iter.rs index 29ea5ff..28d0a58 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -60,6 +60,9 @@ pub struct Iter<'a, T: ?Sized> { _phantom: PhantomData<&'a T> } +unsafe impl<'a, T: Send> Send for Iter<'a, T> {} +unsafe impl<'a, T: Sync> Sync for Iter<'a, T> {} + impl<'a, T: ?Sized> Iter<'a, T> { /// Creates a new by-ref iterator over a [`Vec`]. usually seen as [`Vec::iter`]. pub fn new(vec: &'a Vec) -> Self { @@ -100,6 +103,9 @@ pub struct IterMut<'a, T: ?Sized> { _phantom: PhantomData<&'a mut T> } +unsafe impl<'a, T: Send> Send for IterMut<'a, T> {} +unsafe impl<'a, T: Sync> Sync for IterMut<'a, T> {} + impl<'a, T: ?Sized> IterMut<'a, T> { /// Creates a new by-mut iterator over a [`Vec`]. usually seen as [`Vec::iter_mut`]. pub fn new(vec: &'a mut Vec) -> Self { @@ -141,6 +147,9 @@ pub struct IntoIter { base: BaseIter } +unsafe impl Send for IntoIter {} +unsafe impl Sync for IntoIter {} + impl IntoIter { /// Creates a new by-value iterator over a [`Vec`]. usually seen as [`Vec::into_iter`]. pub fn new(vec: Vec) -> Self { diff --git a/src/lib.rs b/src/lib.rs index 75c11fd..9cfc639 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,11 +2,14 @@ //! //! 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. +//! This crate is currently stable, but lacks some functionality. To enable this functionality, use the `"unstable"` crate feature, which depends on the following nightly features: //! - `#![feature(coerce_unsized)]`, to add trait bounds for types which can be unsized to another type. //! +//! and enables the following functionality (note: these links are probably broken): +//! - [`Vec::push_unsize`] +//! - [`Vec::unsize`] +//! - [`Vec::extend_unsize`] +//! //! # Examples //! //! You can create a vector with [`Vec::new`]: @@ -49,7 +52,7 @@ //! # assert_eq!(format!("{:?}", vec), "[1, 2, 3]"); //! ``` //! -//! ...and with [`push_box`] and [`push_unsize`]: +//! ...and with [`push_box`] and [`push_unsize`] ([`push_unsize_stable`] without the `"unstable"` feature): //! //! ``` //! # use dyn_vec::prelude::{*, vec}; @@ -59,24 +62,27 @@ //! vec.push_box(Box::new("foo")); //! vec.push_box(Box::new(true)); //! -//! vec.push_unsize(2); -//! vec.push_unsize("bar"); -//! vec.push_unsize(false); +//! // these closures are only needed for the `_stable` versions +//! vec.push_unsize_stable(2, |v| v as _); +//! vec.push_unsize_stable("bar", |v| v as _); +//! vec.push_unsize_stable(false, |v| v as _); //! # assert_eq!(format!("{:?}", vec), r#"[1, "foo", true, 2, "bar", false]"#); //! ``` //! -//! Finally, a vector can be `unsize`d to another vector: +//! Finally, a vector can be [`unsize`]d to another vector ([`unsize_stable`] on stable): //! //! ``` //! # use dyn_vec::prelude::{*, vec}; //! # use std::fmt::Debug; //! let vec: Vec = vec![1, 2, 3]; -//! // vec.push_unsize("foo"); // not yet... -//! let mut vec: Vec = vec.unsize(); -//! vec.push_unsize("foo"); // now we can! +//! // vec.push_unsize_stable("foo", |v| v as _); // not yet... +//! let mut vec: Vec = vec.unsize_stable(|v| v as _); +//! vec.push_unsize_stable("foo", |v| v as _); // now we can! //! # assert_eq!(format!("{:?}", vec), r#"[1, 2, 3, "foo"]"#); //! ``` //! +//! To use the `_stable` variations, one can generally add the argument `|v| v as _`. +//! //! # Data Layout //! //! ```text @@ -101,12 +107,17 @@ //! ``` //! //! [`Vec`]: Vec +//! [`Vec::push_unsize`]: Vec::push_unsize +//! [`Vec::unsize`]: Vec::unsize +//! [`Vec::extend_unsize`]: Vec::extend_unsize //! [`push_box`]: Vec::push_box //! [`push_unsize`]: Vec::push_unsize +//! [`push_unsize_stable`]: Vec::push_unsize_stable +//! [`unsize`]: Vec::unsize +//! [`unsize_stable`]: Vec::unsize_stable -#![feature(ptr_metadata)] -#![feature(layout_for_ptr)] -#![feature(coerce_unsized)] +#![cfg_attr(feature = "unstable", feature(coerce_unsized))] +#![feature(backtrace)] // for debugging, REMOVE IT BEFORE COMMIT // for `mod bad_things`, still a wip // #![allow(incomplete_features)] // #![feature(specialization)] @@ -131,12 +142,17 @@ use std::{ ptr::{NonNull, drop_in_place}, marker::PhantomData, alloc::{Layout, alloc, dealloc}, - mem::{self, size_of, size_of_val, align_of_val, size_of_val_raw, align_of_val_raw}, + mem::{self, size_of, size_of_val, align_of_val}, slice, - ops::{CoerceUnsized, Index, IndexMut}, + ops::{Index, IndexMut}, any::Any }; +#[cfg(feature = "unstable")] +use std::ops::CoerceUnsized; + +type Coercer = for<'a> fn(&'a T) -> &'a U; + mod ptr_ext; #[allow(clippy::wildcard_imports)] use ptr_ext::*; @@ -218,21 +234,31 @@ impl Vec { pub fn push_box(&mut self, v: Box) { let ptr = Box::into_raw(v); unsafe { + let layout = Layout::for_value(&*ptr); // ref it *before* its logically uninit self.push_raw(ptr); - dealloc(ptr.cast(), Layout::for_value_raw(ptr)); + dealloc(ptr.cast(), layout); } } /// Appends a sized element of type `U` to the end of the vector, given that it can be `CoerceUnsized` to a `T`. + #[cfg(feature = "unstable")] pub fn push_unsize(&mut self, v: U) where for<'a> &'a U: CoerceUnsized<&'a T> { - let v_unsized: &T = &v; + // TODO: maybe make this not call the stable version for perf? + self.push_unsize_stable(v, |v| v as _); + } + + /// Appends a sized element of type `U` to the end of the vector, given that it can be `CoerceUnsized` to a `T`. + /// + /// The coercion is done through a closure, since `CoerceUnsized` is unstable. Usually you can pass `|v| v as _`. + pub fn push_unsize_stable(&mut self, v: U, coercer: Coercer) { + let v_unsized: &T = coercer(&v); unsafe { self.push_raw(v_unsized) }; mem::forget(v); } unsafe fn push_raw(&mut self, v: *const T) { if !self.will_fit(&*v) { - let new_alloc_size = self.capacity * 2 + size_of_val_raw(v) * 2 + size_of::>(); + let new_alloc_size = self.capacity * 2 + size_of_val(&*v) * 2 + size_of::>(); self.realloc(new_alloc_size); } @@ -290,10 +316,10 @@ impl Vec { for i in 0..self.len { let v = self.get_ptr(i); - ptr = ptr.align_up(align_of_val_raw(v)); + ptr = ptr.align_up(align_of_val(&*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)); + ptr = ptr.wrapping_add(size_of_val(&*v)); } self.end_ptr = NonNull::new_unchecked(ptr); @@ -415,7 +441,16 @@ 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> { + #[cfg(feature = "unstable")] + pub fn unsize(self) -> Vec where for<'a> &'a T: CoerceUnsized<&'a U> { + // TODO: maybe make this not call the stable version for perf? + self.unsize_stable(|v| v as _) + } + + /// Converts a `Vec` into a `Vec`, given that `T` can be `CoerceUnsized` into `U`. + /// + /// The coercion is done through a closure, since `CoerceUnsized` is unstable. Usually you can pass `|v| v as _`. + pub fn unsize_stable(mut self, coercer: Coercer) -> Vec { if size_of::>() > size_of::>() { let elem_size = self.end_ptr.as_ptr().addr() - self.ptr.as_ptr().addr(); let extra_size = self.len * size_of::>(); @@ -439,7 +474,7 @@ impl Vec { 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) } + unsafe { new_vec.set_extra_from_ptr(i, coercer(current)) } } } else { // new extra smaller or same size as old extra, must go from front to back @@ -447,7 +482,7 @@ impl Vec { 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) } + unsafe { new_vec.set_extra_from_ptr(i, coercer(current)) } } } @@ -464,9 +499,20 @@ impl Vec { /// Extends this vector with an iterator. /// /// Similar to [`Extend::extend`], but seperate to prevent conflicting implementations. + #[cfg(feature = "unstable")] pub fn extend_unsize>(&mut self, iter: I) where for<'a> &'a U: CoerceUnsized<&'a T> { + // TODO: maybe make this not call the stable version for perf? + self.extend_unsize_stable(iter, |v| v as _); + } + + /// Extends this vector with an iterator. + /// + /// Similar to [`Extend::extend`], but seperate to prevent conflicting implementations. + /// + /// The coercion is done through a closure, since `CoerceUnsized` is unstable. Usually you can pass `|v| v as _`. + pub fn extend_unsize_stable>(&mut self, iter: I, coercer: Coercer) { for item in iter { - self.push_unsize(item); + self.push_unsize_stable(item, coercer); } } @@ -489,7 +535,7 @@ impl Vec { 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)); + new_ptr = new_ptr.align_up(align_of_val(&*next_ptr)); // if its the same, we can break as the rest will be useless if new_ptr == next_ptr { break } @@ -581,9 +627,10 @@ impl Vec { impl Drop for Vec { fn drop(&mut self) { unsafe { - for i in 0..self.len { - drop_in_place(self.get_unchecked_mut(i)); + for el in self.iter_mut() { + drop_in_place(el); } + self.dealloc(); } } @@ -639,7 +686,8 @@ macro_rules! vec { }}; (unsized: $($elem:expr),+ $(,)?) => {{ let mut vec = $crate::Vec::new(); - $(vec.push_unsize($elem);)+ + // TODO: when stuff stabalizes change this + $(vec.push_unsize_stable($elem, |v| v as _);)+ vec }}; ($elem:expr; $n:expr) => { diff --git a/src/ptr_ext.rs b/src/ptr_ext.rs index 90cb4a2..d3556b8 100644 --- a/src/ptr_ext.rs +++ b/src/ptr_ext.rs @@ -1,4 +1,4 @@ -use std::{ptr::{self, Pointee}, mem::size_of_val_raw, alloc::{alloc, Layout}}; +use std::{alloc::{alloc, Layout}, mem::size_of_val, ptr::addr_of_mut}; use self::__priv::Sealed; @@ -37,7 +37,7 @@ impl PtrExt for *const T { type Pointee = T; fn addr(self) -> usize { - self.to_raw_parts().0 as usize + self.data_ptr() as usize } fn align_up(self, align: usize) -> Self { @@ -48,12 +48,15 @@ impl PtrExt for *const T { self.with_addr(addr.addr()) } - fn with_addr(self, addr: usize) -> Self { - ptr::from_raw_parts(addr as _, self.to_raw_parts().1) + fn with_addr(mut self, addr: usize) -> Self { + // TODO: aaaaand this is cheating. sorry yall, cant do this until `set_ptr_value` lands + unsafe { *addr_of_mut!(self).cast() = addr } + self } unsafe fn get_end(self) -> Self { - self.add_bytes(size_of_val_raw(self)) + // TODO: _raw + self.add_bytes(size_of_val(&*self)) } fn add_bytes(self, offset: usize) -> Self { @@ -69,11 +72,13 @@ impl PtrExt for *const T { } unsafe fn copy_val_to(self, dest: *mut U) { - self.copy_bytes_to(dest, size_of_val_raw(self)); + // TODO: _raw + self.copy_bytes_to(dest, size_of_val(&*self)); } unsafe fn read_to_box(self) -> Box { - let alloc = alloc(Layout::for_value_raw(self)); + // TODO: _raw + let alloc = alloc(Layout::for_value(&*self)); self.copy_val_to(alloc); Box::from_raw(alloc.with_meta_from(self)) } @@ -83,7 +88,7 @@ impl PtrExt for *mut T { type Pointee = T; fn addr(self) -> usize { - self.to_raw_parts().0 as usize + self.data_ptr() as usize } fn align_up(self, align: usize) -> Self { @@ -94,12 +99,15 @@ impl PtrExt for *mut T { self.with_addr(addr.addr()) } - fn with_addr(self, addr: usize) -> Self { - ptr::from_raw_parts_mut(addr as _, self.to_raw_parts().1) + fn with_addr(mut self, addr: usize) -> Self { + // TODO: aaaaand this is cheating. sorry yall, cant do this until `set_ptr_value` lands + unsafe { *addr_of_mut!(self).cast() = addr } + self } unsafe fn get_end(self) -> Self { - self.add_bytes(size_of_val_raw(self)) + // TODO: _raw + self.add_bytes(size_of_val(&*self)) } fn add_bytes(self, offset: usize) -> Self { @@ -115,11 +123,13 @@ impl PtrExt for *mut T { } unsafe fn copy_val_to(self, dest: *mut U) { - self.copy_bytes_to(dest, size_of_val_raw(self)); + // TODO: _raw + self.copy_bytes_to(dest, size_of_val(&*self)); } unsafe fn read_to_box(self) -> Box { - let alloc = alloc(Layout::for_value_raw(self)); + // TODO: _raw + let alloc = alloc(Layout::for_value(&*self)); self.copy_val_to(alloc); Box::from_raw(alloc.with_meta_from(self)) } @@ -129,22 +139,20 @@ impl PtrExt for *mut T { pub trait ConstPtrExt: Sealed { fn data_ptr(self) -> *const (); - fn with_meta(self, meta: ::Metadata) -> *const U; - fn with_meta_from(self, ptr: *const U) -> *const U; } impl ConstPtrExt for *const T { fn data_ptr(self) -> *const () { - self.to_raw_parts().0 + self.cast() } - fn with_meta(self, meta: ::Metadata) -> *const U { - ptr::from_raw_parts(self.data_ptr(), meta) - } + // fn with_meta(self, meta: ::Metadata) -> *const U { + // ptr::from_raw_parts(self.data_ptr(), meta) + // } fn with_meta_from(self, ptr: *const U) -> *const U { - self.with_meta(ptr.to_raw_parts().1) + ptr.with_addr_from(self) } } @@ -152,21 +160,21 @@ impl ConstPtrExt for *const T { pub trait MutPtrExt: Sealed { fn data_ptr(self) -> *mut (); - fn with_meta(self, meta: ::Metadata) -> *mut U; + // fn with_meta(self, meta: ::Metadata) -> *mut U; fn with_meta_from(self, ptr: *const U) -> *mut U; } impl MutPtrExt for *mut T { fn data_ptr(self) -> *mut () { - self.to_raw_parts().0 + self.cast() } - fn with_meta(self, meta: ::Metadata) -> *mut U { - ptr::from_raw_parts_mut(self.data_ptr(), meta) - } + // fn with_meta(self, meta: ::Metadata) -> *mut U { + // ptr::from_raw_parts_mut(self.data_ptr(), meta) + // } fn with_meta_from(self, ptr: *const U) -> *mut U { - self.with_meta(ptr.to_raw_parts().1) + ptr.with_addr_from(self) as _ } } \ No newline at end of file diff --git a/src/test.rs b/src/test.rs index a46acb9..ba26d03 100644 --- a/src/test.rs +++ b/src/test.rs @@ -32,9 +32,9 @@ fn box_push() { #[test] fn unsize_push() { let mut vec: Vec = Vec::new(); - vec.push_unsize(1); - vec.push_unsize(String::from("foo")); - vec.push_unsize(true); + vec.push_unsize_stable(1, |v| v as _); + vec.push_unsize_stable(String::from("foo"), |v| v as _); + vec.push_unsize_stable(true, |v| v as _); assert_eq!(vec.debug(), "[1, \"foo\", true]"); } @@ -69,11 +69,11 @@ fn dropped() { let vec: Vec = vec![unsized: 1, FunkyDrop, true]; - assert_eq!(DROPPED.load(Ordering::SeqCst), false); + assert!(!DROPPED.load(Ordering::SeqCst)); drop(vec); - assert_eq!(DROPPED.load(Ordering::SeqCst), true); + assert!(DROPPED.load(Ordering::SeqCst)); } #[test] @@ -92,7 +92,7 @@ fn index() { assert_eq!(vec[0], 3); assert_eq!(vec[1], 5); assert_eq!(vec[2], 7); - vec[3]; + let _ = vec[3]; } #[test] @@ -128,21 +128,24 @@ fn iteration() { #[test] fn zst() { - let vec: Vec<()> = vec![(), (), ()]; + #[derive(Debug, PartialEq, Eq)] + struct Zst; - assert_eq!(vec[1], ()); + let vec: Vec = vec![Zst, Zst, Zst]; + + assert_eq!(vec[1], Zst); for el in vec.iter() { - drop(el); + let _ = el; } - assert_eq!(vec.debug(), "[(), (), ()]"); + assert_eq!(vec.debug(), "[Zst, Zst, Zst]"); - let vec: Vec = vec![unsized: (), (), ()]; + let vec: Vec = vec![unsized: Zst, (), Zst]; assert_eq!(vec[1].debug(), "()"); for el in vec.iter() { - drop(el); + let _ = el; } - assert_eq!(vec.debug(), "[(), (), ()]"); + assert_eq!(vec.debug(), "[Zst, (), Zst]"); } #[test] @@ -190,8 +193,8 @@ fn with_capacity() { #[test] fn unsize() { let vec = vec![1, 2, 3]; - let mut vec: Vec = vec.unsize(); - vec.push_unsize(String::from("foo")); + let mut vec: Vec = vec.unsize_stable(|v| v as _); + vec.push_unsize_stable(String::from("foo"), |v| v as _); assert_eq!(vec.debug(), "[1, 2, 3, \"foo\"]"); }