diff --git a/src/arc.rs b/src/arc.rs index a5f09d8..f06d3e6 100644 --- a/src/arc.rs +++ b/src/arc.rs @@ -39,6 +39,23 @@ pub(crate) struct ArcInner { unsafe impl Send for ArcInner {} unsafe impl Sync for ArcInner {} +impl ArcInner { + /// Compute the offset of the `data` field within `ArcInner`. + /// + /// # Safety + /// + /// - The pointer must be created from `Arc::into_raw` or similar functions + /// - The pointee must be initialized (`&*value` must not be UB) + unsafe fn offset_of_data(value: *const T) -> usize { + // We can use `Layout::for_value_raw` when it is stable. + let value = &*value; + + let layout = Layout::new::(); + let (_, offset) = layout.extend(Layout::for_value(value)).unwrap(); + offset + } +} + /// An atomically reference counted shared pointer /// /// See the documentation for [`Arc`] in the standard library. Unlike the @@ -71,26 +88,6 @@ impl Arc { } } - /// Reconstruct the `Arc` from a raw pointer obtained from into_raw() - /// - /// Note: This raw pointer will be offset in the allocation and must be preceded - /// by the atomic count. - /// - /// It is recommended to use OffsetArc for this - /// - /// # Safety - /// - The given pointer must be a valid pointer to `T` that came from [`Arc::into_raw`]. - /// - After `from_raw`, the pointer must not be accessed. - #[inline] - pub unsafe fn from_raw(ptr: *const T) -> Self { - // FIXME: when `byte_sub` is stabilized, this can accept T: ?Sized. - - // To find the corresponding pointer to the `ArcInner` we need - // to subtract the offset of the `data` field from the pointer. - let ptr = (ptr as *const u8).sub(offset_of!(ArcInner, data)); - Arc::from_raw_inner(ptr as *mut ArcInner) - } - /// Temporarily converts |self| into a bonafide OffsetArc and exposes it to the /// provided callback. The refcount is not modified. #[inline(always)] @@ -160,15 +157,7 @@ impl Arc<[T]> { /// - The given pointer must be a valid pointer to `[T]` that came from [`Arc::into_raw`]. /// - After `from_raw_slice`, the pointer must not be accessed. pub unsafe fn from_raw_slice(ptr: *const [T]) -> Self { - let len = (*ptr).len(); - // Assuming the offset of `T` in `ArcInner` is the same - // as as offset of `[T]` in `ArcInner<[T]>`. - // (`offset_of!` macro requires `Sized`.) - let arc_inner_ptr = (ptr as *const u8).sub(offset_of!(ArcInner, data)); - // Synthesize the fat pointer: the pointer metadata for `Arc<[T]>` - // is the same as the pointer metadata for `[T]`: the length. - let fake_slice = ptr::slice_from_raw_parts_mut(arc_inner_ptr as *mut T, len); - Arc::from_raw_inner(fake_slice as *mut ArcInner<[T]>) + Arc::from_raw(ptr) } } @@ -184,6 +173,33 @@ impl Arc { this.as_ptr() } + /// Reconstruct the `Arc` from a raw pointer obtained from into_raw() + /// + /// Note: This raw pointer will be offset in the allocation and must be preceded + /// by the atomic count. + /// + /// It is recommended to use OffsetArc for this + /// + /// # Safety + /// - The given pointer must be a valid pointer to `T` that came from [`Arc::into_raw`]. + /// - After `from_raw`, the pointer must not be accessed. + #[inline] + pub unsafe fn from_raw(ptr: *const T) -> Self { + // To find the corresponding pointer to the `ArcInner` we need + // to subtract the offset of the `data` field from the pointer. + + // SAFETY: `ptr` comes from `Arc`, so it must be initialized. + let offset_of_data = ArcInner::::offset_of_data(ptr); + + // SAFETY: if `ptr` comes from `Arc`, there's no overflow here, + // and resulting pointer is within the allocation. + let arc_inner_ptr = ptr.byte_sub(offset_of_data); + + // SAFETY: `Arc::from_raw` contract requires that `ptr` comes from `Arc::into_raw`, + // so here we reconstruct the `Arc` back. + Arc::from_raw_inner(arc_inner_ptr as *mut ArcInner) + } + /// Returns the raw pointer. /// /// Same as into_raw except `self` isn't consumed. @@ -1050,6 +1066,25 @@ mod tests { assert_eq!(1, Arc::strong_count(&arc2)); } + #[test] + fn test_into_raw_from_raw_dst() { + trait AnInteger { + fn get_me_an_integer(&self) -> u64; + } + + impl AnInteger for u32 { + fn get_me_an_integer(&self) -> u64 { + *self as u64 + } + } + + let arc = Arc::::new(19); + let data = Arc::into_raw(arc); + let data: *const dyn AnInteger = data as *const _; + let arc: Arc = unsafe { Arc::from_raw(data) }; + assert_eq!(19, arc.get_me_an_integer()); + } + #[allow(dead_code)] const fn is_partial_ord() {} diff --git a/src/lib.rs b/src/lib.rs index 7b3886c..ed91ff2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,30 +34,6 @@ extern crate stable_deref_trait; #[cfg(feature = "unsize")] extern crate unsize; -/// Calculates the offset of the specified field from the start of the named struct. -/// This macro is impossible to be const until feature(const_ptr_offset_from) is stable. -macro_rules! offset_of { - ($ty: path, $field: tt) => {{ - // ensure the type is a named struct - // ensure the field exists and is accessible - let $ty { $field: _, .. }; - - let uninit = <::core::mem::MaybeUninit<$ty>>::uninit(); // const since 1.36 - - let base_ptr: *const $ty = uninit.as_ptr(); // const since 1.59 - - #[allow(unused_unsafe)] - let field_ptr = unsafe { ::core::ptr::addr_of!((*base_ptr).$field) }; // since 1.51 - - // // the const version requires feature(const_ptr_offset_from) - // // https://github.com/rust-lang/rust/issues/92980 - // #[allow(unused_unsafe)] - // unsafe { (field_ptr as *const u8).offset_from(base_ptr as *const u8) as usize } - - (field_ptr as usize) - (base_ptr as usize) - }}; -} - mod arc; mod arc_borrow; #[cfg(feature = "arc-swap")]