This commit is contained in:
missing 2022-06-08 23:00:11 -05:00
parent d46bdc4a57
commit 192099e60b
7 changed files with 156 additions and 71 deletions

View File

@ -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 = []

View File

@ -1,2 +1,5 @@
# dyn_vec
# A `Vec<T: ?Sized>`
A dynamic length collection of unsized elements, akin to `std::vec::Vec`.
Check the crate-level documentation for more info.

View File

@ -63,6 +63,17 @@ impl<T: PartialEq<U>, U, const N: usize> PartialEq<Vec<T>> for [U; N] {
}
}
#[cfg(not(feature = "unstable"))]
impl<T: ?Sized> Extend<Box<T>> for Vec<T> {
fn extend<I: IntoIterator<Item = Box<T>>>(&mut self, iter: I) {
for item in iter {
// TODO: optmize
self.push_box(item);
}
}
}
#[cfg(feature = "unstable")]
impl<T: ?Sized, U: ?Sized> Extend<Box<U>> for Vec<T> where Box<U>: CoerceUnsized<Box<T>> {
fn extend<I: IntoIterator<Item = Box<U>>>(&mut self, iter: I) {
for item in iter {

View File

@ -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<T>) -> 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<T>) -> Self {
@ -141,6 +147,9 @@ pub struct IntoIter<T: ?Sized> {
base: BaseIter<T>
}
unsafe impl<T: Send> Send for IntoIter<T> {}
unsafe impl<T: Sync> Sync for IntoIter<T> {}
impl<T: ?Sized> IntoIter<T> {
/// Creates a new by-value iterator over a [`Vec`]. usually seen as [`Vec::into_iter`].
pub fn new(vec: Vec<T>) -> Self {

View File

@ -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<i32> = vec![1, 2, 3];
//! // vec.push_unsize("foo"); // not yet...
//! let mut vec: Vec<dyn Debug> = vec.unsize();
//! vec.push_unsize("foo"); // now we can!
//! // vec.push_unsize_stable("foo", |v| v as _); // not yet...
//! let mut vec: Vec<dyn Debug> = 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<T: ?Sized>`]: 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<T, U> = for<'a> fn(&'a T) -> &'a U;
mod ptr_ext;
#[allow(clippy::wildcard_imports)]
use ptr_ext::*;
@ -218,21 +234,31 @@ impl<T: ?Sized> Vec<T> {
pub fn push_box(&mut self, v: Box<T>) {
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<U>(&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<U>(&mut self, v: U, coercer: Coercer<U, T>) {
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::<Extra<T>>();
let new_alloc_size = self.capacity * 2 + size_of_val(&*v) * 2 + size_of::<Extra<T>>();
self.realloc(new_alloc_size);
}
@ -290,10 +316,10 @@ impl<T: ?Sized> Vec<T> {
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<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> {
#[cfg(feature = "unstable")]
pub fn unsize<U: ?Sized>(self) -> Vec<U> 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<T: Sized>` into a `Vec<U: ?Sized>`, 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<U: ?Sized>(mut self, coercer: Coercer<T, U>) -> Vec<U> {
if size_of::<Extra<U>>() > size_of::<Extra<T>>() {
let elem_size = self.end_ptr.as_ptr().addr() - self.ptr.as_ptr().addr();
let extra_size = self.len * size_of::<Extra<U>>();
@ -439,7 +474,7 @@ impl<T: ?Sized> Vec<T> {
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<T: ?Sized> Vec<T> {
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<T: ?Sized> Vec<T> {
/// Extends this vector with an iterator.
///
/// Similar to [`Extend::extend`], but seperate to prevent conflicting implementations.
#[cfg(feature = "unstable")]
pub fn extend_unsize<U, I: IntoIterator<Item = U>>(&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<U, I: IntoIterator<Item = U>>(&mut self, iter: I, coercer: Coercer<U, T>) {
for item in iter {
self.push_unsize(item);
self.push_unsize_stable(item, coercer);
}
}
@ -489,7 +535,7 @@ impl<T: ?Sized> Vec<T> {
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<dyn Any> {
impl<T: ?Sized> Drop for Vec<T> {
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) => {

View File

@ -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<T: ?Sized> 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<T: ?Sized> 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<T: ?Sized> PtrExt for *const T {
}
unsafe fn copy_val_to<U: ?Sized>(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<Self::Pointee> {
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<T: ?Sized> 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<T: ?Sized> 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<T: ?Sized> PtrExt for *mut T {
}
unsafe fn copy_val_to<U: ?Sized>(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<Self::Pointee> {
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<T: ?Sized> PtrExt for *mut T {
pub trait ConstPtrExt: Sealed {
fn data_ptr(self) -> *const ();
fn with_meta<U: ?Sized>(self, meta: <U as Pointee>::Metadata) -> *const U;
fn with_meta_from<U: ?Sized>(self, ptr: *const U) -> *const U;
}
impl<T: ?Sized> ConstPtrExt for *const T {
fn data_ptr(self) -> *const () {
self.to_raw_parts().0
self.cast()
}
fn with_meta<U: ?Sized>(self, meta: <U as Pointee>::Metadata) -> *const U {
ptr::from_raw_parts(self.data_ptr(), meta)
}
// fn with_meta<U: ?Sized>(self, meta: <U as Pointee>::Metadata) -> *const U {
// ptr::from_raw_parts(self.data_ptr(), meta)
// }
fn with_meta_from<U: ?Sized>(self, ptr: *const U) -> *const U {
self.with_meta(ptr.to_raw_parts().1)
ptr.with_addr_from(self)
}
}
@ -152,21 +160,21 @@ impl<T: ?Sized> ConstPtrExt for *const T {
pub trait MutPtrExt: Sealed {
fn data_ptr(self) -> *mut ();
fn with_meta<U: ?Sized>(self, meta: <U as Pointee>::Metadata) -> *mut U;
// fn with_meta<U: ?Sized>(self, meta: <U as Pointee>::Metadata) -> *mut U;
fn with_meta_from<U: ?Sized>(self, ptr: *const U) -> *mut U;
}
impl<T: ?Sized> MutPtrExt for *mut T {
fn data_ptr(self) -> *mut () {
self.to_raw_parts().0
self.cast()
}
fn with_meta<U: ?Sized>(self, meta: <U as Pointee>::Metadata) -> *mut U {
ptr::from_raw_parts_mut(self.data_ptr(), meta)
}
// fn with_meta<U: ?Sized>(self, meta: <U as Pointee>::Metadata) -> *mut U {
// ptr::from_raw_parts_mut(self.data_ptr(), meta)
// }
fn with_meta_from<U: ?Sized>(self, ptr: *const U) -> *mut U {
self.with_meta(ptr.to_raw_parts().1)
ptr.with_addr_from(self) as _
}
}

View File

@ -32,9 +32,9 @@ fn box_push() {
#[test]
fn unsize_push() {
let mut vec: Vec<dyn Debug> = 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<dyn Debug> = 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<Zst> = 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<dyn Debug> = vec![unsized: (), (), ()];
let vec: Vec<dyn Debug> = 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<dyn Debug> = vec.unsize();
vec.push_unsize(String::from("foo"));
let mut vec: Vec<dyn Debug> = vec.unsize_stable(|v| v as _);
vec.push_unsize_stable(String::from("foo"), |v| v as _);
assert_eq!(vec.debug(), "[1, 2, 3, \"foo\"]");
}