| use alloc::alloc::Layout; |
| use alloc::boxed::Box; |
| use alloc::string::String; |
| use alloc::vec::Vec; |
| use core::cmp::Ordering; |
| use core::iter::{ExactSizeIterator, Iterator}; |
| use core::marker::PhantomData; |
| use core::mem::{self, ManuallyDrop}; |
| use core::ptr::{self, addr_of_mut}; |
| use core::usize; |
| |
| use super::{Arc, ArcInner}; |
| |
| /// Structure to allow Arc-managing some fixed-sized data and a variably-sized |
| /// slice in a single allocation. |
| #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)] |
| #[repr(C)] |
| pub struct HeaderSlice<H, T: ?Sized> { |
| /// The fixed-sized data. |
| pub header: H, |
| |
| /// The dynamically-sized data. |
| pub slice: T, |
| } |
| |
| impl<H, T> Arc<HeaderSlice<H, [T]>> { |
| /// Creates an Arc for a HeaderSlice using the given header struct and |
| /// iterator to generate the slice. The resulting Arc will be fat. |
| pub fn from_header_and_iter<I>(header: H, mut items: I) -> Self |
| where |
| I: Iterator<Item = T> + ExactSizeIterator, |
| { |
| assert_ne!(mem::size_of::<T>(), 0, "Need to think about ZST"); |
| |
| let num_items = items.len(); |
| |
| let inner = Arc::allocate_for_header_and_slice(num_items); |
| |
| unsafe { |
| // Write the data. |
| // |
| // Note that any panics here (i.e. from the iterator) are safe, since |
| // we'll just leak the uninitialized memory. |
| ptr::write(&mut ((*inner.as_ptr()).data.header), header); |
| let mut current = (*inner.as_ptr()).data.slice.as_mut_ptr(); |
| for _ in 0..num_items { |
| ptr::write( |
| current, |
| items |
| .next() |
| .expect("ExactSizeIterator over-reported length"), |
| ); |
| current = current.offset(1); |
| } |
| assert!( |
| items.next().is_none(), |
| "ExactSizeIterator under-reported length" |
| ); |
| } |
| |
| // Safety: ptr is valid & the inner structure is fully initialized |
| Arc { |
| p: inner, |
| phantom: PhantomData, |
| } |
| } |
| |
| /// Creates an Arc for a HeaderSlice using the given header struct and |
| /// iterator to generate the slice. The resulting Arc will be fat. |
| pub fn from_header_and_slice(header: H, items: &[T]) -> Self |
| where |
| T: Copy, |
| { |
| assert_ne!(mem::size_of::<T>(), 0, "Need to think about ZST"); |
| |
| let num_items = items.len(); |
| |
| let inner = Arc::allocate_for_header_and_slice(num_items); |
| |
| unsafe { |
| // Write the data. |
| ptr::write(&mut ((*inner.as_ptr()).data.header), header); |
| let dst = (*inner.as_ptr()).data.slice.as_mut_ptr(); |
| ptr::copy_nonoverlapping(items.as_ptr(), dst, num_items); |
| } |
| |
| // Safety: ptr is valid & the inner structure is fully initialized |
| Arc { |
| p: inner, |
| phantom: PhantomData, |
| } |
| } |
| |
| /// Creates an Arc for a HeaderSlice using the given header struct and |
| /// vec to generate the slice. The resulting Arc will be fat. |
| pub fn from_header_and_vec(header: H, mut v: Vec<T>) -> Self { |
| let len = v.len(); |
| |
| let inner = Arc::allocate_for_header_and_slice(len); |
| |
| unsafe { |
| // Safety: inner is a valid pointer, so this can't go out of bounds |
| let dst = addr_of_mut!((*inner.as_ptr()).data.header); |
| |
| // Safety: `dst` is valid for writes (just allocated) |
| ptr::write(dst, header); |
| } |
| |
| unsafe { |
| let src = v.as_mut_ptr(); |
| |
| // Safety: inner is a valid pointer, so this can't go out of bounds |
| let dst = addr_of_mut!((*inner.as_ptr()).data.slice) as *mut T; |
| |
| // Safety: |
| // - `src` is valid for reads for `len` (got from `Vec`) |
| // - `dst` is valid for writes for `len` (just allocated, with layout for appropriate slice) |
| // - `src` and `dst` don't overlap (separate allocations) |
| ptr::copy_nonoverlapping(src, dst, len); |
| |
| // Deallocate vec without dropping `T` |
| // |
| // Safety: 0..0 elements are always initialized, 0 <= cap for any cap |
| v.set_len(0); |
| } |
| |
| // Safety: ptr is valid & the inner structure is fully initialized |
| Arc { |
| p: inner, |
| phantom: PhantomData, |
| } |
| } |
| } |
| |
| impl<H> Arc<HeaderSlice<H, str>> { |
| /// Creates an Arc for a HeaderSlice using the given header struct and |
| /// a str slice to generate the slice. The resulting Arc will be fat. |
| pub fn from_header_and_str(header: H, string: &str) -> Self { |
| let bytes = Arc::from_header_and_slice(header, string.as_bytes()); |
| |
| // Safety: `ArcInner` and `HeaderSlice` are `repr(C)`, `str` has the same layout as `[u8]`, |
| // thus it's ok to "transmute" between `Arc<HeaderSlice<H, [u8]>>` and `Arc<HeaderSlice<H, str>>`. |
| // |
| // `bytes` are a valid string since we've just got them from a valid `str`. |
| unsafe { Arc::from_raw_inner(Arc::into_raw_inner(bytes) as _) } |
| } |
| } |
| |
| /// Header data with an inline length. Consumers that use HeaderWithLength as the |
| /// Header type in HeaderSlice can take advantage of ThinArc. |
| #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] |
| #[repr(C)] |
| pub struct HeaderWithLength<H> { |
| /// The fixed-sized data. |
| pub header: H, |
| |
| /// The slice length. |
| pub length: usize, |
| } |
| |
| impl<H> HeaderWithLength<H> { |
| /// Creates a new HeaderWithLength. |
| #[inline] |
| pub fn new(header: H, length: usize) -> Self { |
| HeaderWithLength { header, length } |
| } |
| } |
| |
| impl<T: ?Sized> From<Arc<HeaderSlice<(), T>>> for Arc<T> { |
| fn from(this: Arc<HeaderSlice<(), T>>) -> Self { |
| debug_assert_eq!( |
| Layout::for_value::<HeaderSlice<(), T>>(&this), |
| Layout::for_value::<T>(&this.slice) |
| ); |
| |
| // Safety: `HeaderSlice<(), T>` and `T` has the same layout |
| unsafe { Arc::from_raw_inner(Arc::into_raw_inner(this) as _) } |
| } |
| } |
| |
| impl<T: ?Sized> From<Arc<T>> for Arc<HeaderSlice<(), T>> { |
| fn from(this: Arc<T>) -> Self { |
| // Safety: `T` and `HeaderSlice<(), T>` has the same layout |
| unsafe { Arc::from_raw_inner(Arc::into_raw_inner(this) as _) } |
| } |
| } |
| |
| impl<T: Copy> From<&[T]> for Arc<[T]> { |
| fn from(slice: &[T]) -> Self { |
| Arc::from_header_and_slice((), slice).into() |
| } |
| } |
| |
| impl From<&str> for Arc<str> { |
| fn from(s: &str) -> Self { |
| Arc::from_header_and_str((), s).into() |
| } |
| } |
| |
| impl From<String> for Arc<str> { |
| fn from(s: String) -> Self { |
| Self::from(&s[..]) |
| } |
| } |
| |
| // FIXME: once `pointer::with_metadata_of` is stable or |
| // implementable on stable without assuming ptr layout |
| // this will be able to accept `T: ?Sized`. |
| impl<T> From<Box<T>> for Arc<T> { |
| fn from(b: Box<T>) -> Self { |
| let layout = Layout::for_value::<T>(&b); |
| |
| // Safety: the closure only changes the type of the pointer |
| let inner = unsafe { Self::allocate_for_layout(layout, |mem| mem as *mut ArcInner<T>) }; |
| |
| unsafe { |
| let src = Box::into_raw(b); |
| |
| // Safety: inner is a valid pointer, so this can't go out of bounds |
| let dst = addr_of_mut!((*inner.as_ptr()).data); |
| |
| // Safety: |
| // - `src` is valid for reads (got from `Box`) |
| // - `dst` is valid for writes (just allocated) |
| // - `src` and `dst` don't overlap (separate allocations) |
| ptr::copy_nonoverlapping(src, dst, 1); |
| |
| // Deallocate box without dropping `T` |
| // |
| // Safety: |
| // - `src` has been got from `Box::into_raw` |
| // - `ManuallyDrop<T>` is guaranteed to have the same layout as `T` |
| drop(Box::<ManuallyDrop<T>>::from_raw(src as _)); |
| } |
| |
| Arc { |
| p: inner, |
| phantom: PhantomData, |
| } |
| } |
| } |
| |
| impl<T> From<Vec<T>> for Arc<[T]> { |
| fn from(v: Vec<T>) -> Self { |
| Arc::from_header_and_vec((), v).into() |
| } |
| } |
| |
| pub(crate) type HeaderSliceWithLength<H, T> = HeaderSlice<HeaderWithLength<H>, T>; |
| |
| impl<H: PartialOrd, T: ?Sized + PartialOrd> PartialOrd for HeaderSliceWithLength<H, T> { |
| fn partial_cmp(&self, other: &Self) -> Option<Ordering> { |
| (&self.header.header, &self.slice).partial_cmp(&(&other.header.header, &other.slice)) |
| } |
| } |
| |
| impl<H: Ord, T: ?Sized + Ord> Ord for HeaderSliceWithLength<H, T> { |
| fn cmp(&self, other: &Self) -> Ordering { |
| (&self.header.header, &self.slice).cmp(&(&other.header.header, &other.slice)) |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use alloc::boxed::Box; |
| use alloc::string::String; |
| use alloc::vec; |
| use core::iter; |
| |
| use crate::{Arc, HeaderSlice}; |
| |
| #[test] |
| fn from_header_and_iter_smoke() { |
| let arc = Arc::from_header_and_iter( |
| (42u32, 17u8), |
| IntoIterator::into_iter([1u16, 2, 3, 4, 5, 6, 7]), |
| ); |
| |
| assert_eq!(arc.header, (42, 17)); |
| assert_eq!(arc.slice, [1, 2, 3, 4, 5, 6, 7]); |
| } |
| |
| #[test] |
| fn from_header_and_slice_smoke() { |
| let arc = Arc::from_header_and_slice((42u32, 17u8), &[1u16, 2, 3, 4, 5, 6, 7]); |
| |
| assert_eq!(arc.header, (42, 17)); |
| assert_eq!(arc.slice, [1u16, 2, 3, 4, 5, 6, 7]); |
| } |
| |
| #[test] |
| fn from_header_and_vec_smoke() { |
| let arc = Arc::from_header_and_vec((42u32, 17u8), vec![1u16, 2, 3, 4, 5, 6, 7]); |
| |
| assert_eq!(arc.header, (42, 17)); |
| assert_eq!(arc.slice, [1u16, 2, 3, 4, 5, 6, 7]); |
| } |
| |
| #[test] |
| fn from_header_and_iter_empty() { |
| let arc = Arc::from_header_and_iter((42u32, 17u8), iter::empty::<u16>()); |
| |
| assert_eq!(arc.header, (42, 17)); |
| assert_eq!(arc.slice, []); |
| } |
| |
| #[test] |
| fn from_header_and_slice_empty() { |
| let arc = Arc::from_header_and_slice((42u32, 17u8), &[1u16; 0]); |
| |
| assert_eq!(arc.header, (42, 17)); |
| assert_eq!(arc.slice, []); |
| } |
| |
| #[test] |
| fn from_header_and_vec_empty() { |
| let arc = Arc::from_header_and_vec((42u32, 17u8), vec![1u16; 0]); |
| |
| assert_eq!(arc.header, (42, 17)); |
| assert_eq!(arc.slice, []); |
| } |
| |
| #[test] |
| fn issue_13_empty() { |
| crate::Arc::from_header_and_iter((), iter::empty::<usize>()); |
| } |
| |
| #[test] |
| fn issue_13_consumption() { |
| let s: &[u8] = &[0u8; 255]; |
| crate::Arc::from_header_and_iter((), s.iter().copied()); |
| } |
| |
| #[test] |
| fn from_header_and_str_smoke() { |
| let a = Arc::from_header_and_str( |
| 42, |
| "The answer to the ultimate question of life, the universe, and everything", |
| ); |
| assert_eq!(a.header, 42); |
| assert_eq!( |
| &a.slice, |
| "The answer to the ultimate question of life, the universe, and everything" |
| ); |
| |
| let empty = Arc::from_header_and_str((), ""); |
| assert_eq!(empty.header, ()); |
| assert_eq!(&empty.slice, ""); |
| } |
| |
| #[test] |
| fn erase_and_create_from_thin_air_header() { |
| let a: Arc<HeaderSlice<(), [u32]>> = Arc::from_header_and_slice((), &[12, 17, 16]); |
| let b: Arc<[u32]> = a.into(); |
| |
| assert_eq!(&*b, [12, 17, 16]); |
| |
| let c: Arc<HeaderSlice<(), [u32]>> = b.into(); |
| |
| assert_eq!(&c.slice, [12, 17, 16]); |
| assert_eq!(c.header, ()); |
| } |
| |
| #[test] |
| fn from_box_and_vec() { |
| let b = Box::new(String::from("xxx")); |
| let b = Arc::<String>::from(b); |
| assert_eq!(&*b, "xxx"); |
| |
| let v = vec![String::from("1"), String::from("2"), String::from("3")]; |
| let v = Arc::<[_]>::from(v); |
| assert_eq!( |
| &*v, |
| [String::from("1"), String::from("2"), String::from("3")] |
| ); |
| |
| let mut v = vec![String::from("1"), String::from("2"), String::from("3")]; |
| v.reserve(10); |
| let v = Arc::<[_]>::from(v); |
| assert_eq!( |
| &*v, |
| [String::from("1"), String::from("2"), String::from("3")] |
| ); |
| } |
| |
| /// It’s possible to make a generic `Arc` wrapper that supports both: |
| /// |
| /// * `T: !Sized` |
| /// * `Arc::make_mut` if `T: Sized` |
| #[test] |
| fn dst_and_make_mut() { |
| struct MyArc<T: ?Sized>(Arc<HeaderSlice<MyHeader, T>>); |
| |
| #[derive(Clone)] |
| struct MyHeader { |
| // Very interesting things go here |
| } |
| |
| // MyArc<str> is possible |
| let dst: MyArc<str> = MyArc(Arc::from_header_and_str(MyHeader {}, "example")); |
| assert_eq!(&dst.0.slice, "example"); |
| |
| // `make_mut` is still available when `T: Sized` |
| let mut answer: MyArc<u32> = MyArc(Arc::new(HeaderSlice { |
| header: MyHeader {}, |
| // Not actually a slice in this case, |
| // but `HeaderSlice` is required to use `from_header_and_str` |
| // and we want the same `MyArc` to support both cases. |
| slice: 6 * 9, |
| })); |
| let mut_ref = Arc::make_mut(&mut answer.0); |
| mut_ref.slice = 42; |
| } |
| } |