From e1b5c625ce2bce5d197aee111b61cad1f7efd406 Mon Sep 17 00:00:00 2001 From: Arthur Silva Date: Sun, 18 Aug 2024 18:22:14 +0200 Subject: [PATCH 1/2] Add UniqueArc::from_header_and_uninit_slice --- src/unique_arc.rs | 63 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/src/unique_arc.rs b/src/unique_arc.rs index 45a9e90..c49249f 100644 --- a/src/unique_arc.rs +++ b/src/unique_arc.rs @@ -183,6 +183,33 @@ impl UniqueArc<[MaybeUninit]> { } } +impl UniqueArc]>> { + /// Creates an Arc for a HeaderSlice using the given header struct and allocated space + /// for an unitialized slice of length `len`. + pub fn from_header_and_uninit_slice(header: H, len: usize) -> Self { + let inner = Arc::allocate_for_header_and_slice(len); + + unsafe { + // Write the header. + ptr::write(&mut ((*inner.as_ptr()).data.header), header); + } + + // Safety: ptr is valid & the inner structure is fully initialized + Self(Arc { + p: inner, + phantom: PhantomData, + }) + } + + /// # Safety + /// + /// Must initialize all fields before calling this function. + #[inline] + pub unsafe fn assume_init_slice_with_header(self) -> UniqueArc> { + unsafe { core::mem::transmute(self) } + } +} + impl TryFrom> for UniqueArc { type Error = Arc; @@ -248,7 +275,7 @@ unsafe impl unsize::CoerciblePtr for UniqueArc { #[cfg(test)] mod tests { - use crate::{Arc, UniqueArc}; + use crate::{Arc, HeaderSliceWithLength, HeaderWithLength, UniqueArc}; use core::{convert::TryFrom, mem::MaybeUninit}; #[test] @@ -278,4 +305,38 @@ mod tests { let arc = unsafe { UniqueArc::assume_init(arc) }; assert_eq!(*arc, 999); } + + #[test] + fn from_header_and_uninit_slice() { + let mut uarc: UniqueArc]>> = + UniqueArc::from_header_and_uninit_slice(HeaderWithLength::new(1, 3), 3); + uarc.slice.fill(MaybeUninit::new(2)); + let arc = unsafe { uarc.assume_init_slice_with_header() }.shareable(); + assert!(arc.is_unique()); + // Using clone to that the layout generated in new_uninit_slice is compatible + // with ArcInner. + let arcs = [ + arc.clone(), + arc.clone(), + arc.clone(), + arc.clone(), + arc.clone(), + ]; + // Similar for ThinArc + let thin = Arc::into_thin(arc.clone()); + assert_eq!(7, Arc::count(&arc)); + // If the layout is not compatible, then the data might be corrupted. + assert_eq!(arc.header.header, 1); + assert_eq!(&arc.slice, [2, 2, 2]); + assert_eq!(thin.header.header, 1); + assert_eq!(&thin.slice, [2, 2, 2]); + + // Drop the arcs and check the count and the content to + // make sure it isn't corrupted. + drop(arcs); + drop(thin); + assert!(arc.is_unique()); + assert_eq!(arc.header.header, 1); + assert_eq!(&arc.slice, [2, 2, 2]); + } } From 2fa198dbf25a1ed5c277c0177131754fcd9ee2bc Mon Sep 17 00:00:00 2001 From: Arthur Silva Date: Thu, 26 Sep 2024 20:30:04 +0200 Subject: [PATCH 2/2] incorporate review feedback --- src/unique_arc.rs | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/unique_arc.rs b/src/unique_arc.rs index c49249f..5afdd6c 100644 --- a/src/unique_arc.rs +++ b/src/unique_arc.rs @@ -5,7 +5,7 @@ use core::iter::FromIterator; use core::marker::PhantomData; use core::mem::{ManuallyDrop, MaybeUninit}; use core::ops::{Deref, DerefMut}; -use core::ptr::{self, NonNull}; +use core::ptr::{self, addr_of_mut, NonNull}; use core::sync::atomic::AtomicUsize; use crate::iterator_as_exact_size_iterator::IteratorAsExactSizeIterator; @@ -160,18 +160,14 @@ impl UniqueArc> { impl UniqueArc<[MaybeUninit]> { /// Create an Arc contains an array `[MaybeUninit]` of `len`. pub fn new_uninit_slice(len: usize) -> Self { - let ptr: NonNull]>>> = - Arc::allocate_for_header_and_slice(len); - - // Safety: + // Safety (although no unsafe is required): // - `ArcInner` is properly allocated and initialized. // - `()` and `[MaybeUninit]` do not require special initialization // - The `Arc` is just created and so -- unique. - unsafe { - let arc: Arc]>> = Arc::from_raw_inner(ptr.as_ptr()); - let arc: Arc<[MaybeUninit]> = arc.into(); - UniqueArc(arc) - } + let arc: Arc]>> = + UniqueArc::from_header_and_uninit_slice((), len).0; + let arc: Arc<[MaybeUninit]> = arc.into(); + UniqueArc(arc) } /// # Safety @@ -186,15 +182,20 @@ impl UniqueArc<[MaybeUninit]> { impl UniqueArc]>> { /// Creates an Arc for a HeaderSlice using the given header struct and allocated space /// for an unitialized slice of length `len`. + #[inline] pub fn from_header_and_uninit_slice(header: H, len: usize) -> Self { - let inner = Arc::allocate_for_header_and_slice(len); + let inner = Arc::]>>::allocate_for_header_and_slice(len); unsafe { - // Write the header. - ptr::write(&mut ((*inner.as_ptr()).data.header), header); + // 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); } - // Safety: ptr is valid & the inner structure is fully initialized + // Safety: ptr is valid & the inner structure is initialized. + // We wrote the header above and the slice can stay unitialized as it's [MaybeUninit] Self(Arc { p: inner, phantom: PhantomData,