From 1cbb2b3a8827a2c7d37c022da1e706229b0e23af Mon Sep 17 00:00:00 2001 From: Murarth Date: Wed, 23 Aug 2017 22:50:00 -0700 Subject: [PATCH] Implement `Arc`/`Rc` raw pointer conversions for `?Sized` * Add `T: ?Sized` bound to {`Arc`,`Rc`}::{`from_raw`,`into_raw`} --- src/liballoc/arc.rs | 43 +++++++++++++++++++++++++++++++++++------- src/liballoc/macros.rs | 19 ------------------- src/liballoc/rc.rs | 42 ++++++++++++++++++++++++++++++++++------- 3 files changed, 71 insertions(+), 33 deletions(-) diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index d734ae6a2cf..4b695ad7c79 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -22,7 +22,7 @@ use core::fmt; use core::cmp::Ordering; use core::intrinsics::abort; -use core::mem::{self, size_of_val, uninitialized}; +use core::mem::{self, align_of_val, size_of_val, uninitialized}; use core::ops::Deref; use core::ops::CoerceUnsized; use core::ptr::{self, Shared}; @@ -324,7 +324,9 @@ pub fn try_unwrap(this: Self) -> Result { Ok(elem) } } +} +impl Arc { /// Consumes the `Arc`, returning the wrapped pointer. /// /// To avoid a memory leak the pointer must be converted back to an `Arc` using @@ -378,16 +380,21 @@ pub fn into_raw(this: Self) -> *const T { /// ``` #[stable(feature = "rc_raw", since = "1.17.0")] 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. - let ptr = (ptr as *const u8).offset(-offset_of!(ArcInner, data)); + // Align the unsized value to the end of the ArcInner. + // Because it is ?Sized, it will always be the last field in memory. + let align = align_of_val(&*ptr); + let layout = Layout::new::>(); + let offset = (layout.size() + layout.padding_needed_for(align)) as isize; + + // Reverse the offset to find the original ArcInner. + let fake_ptr = ptr as *mut ArcInner; + let arc_ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)); + Arc { - ptr: Shared::new_unchecked(ptr as *mut u8 as *mut _), + ptr: Shared::new_unchecked(arc_ptr), } } -} -impl Arc { /// Creates a new [`Weak`][weak] pointer to this value. /// /// [weak]: struct.Weak.html @@ -1491,6 +1498,28 @@ fn into_from_raw() { } } + #[test] + fn test_into_from_raw_unsized() { + use std::fmt::Display; + use std::string::ToString; + + let arc: Arc = Arc::from("foo"); + + let ptr = Arc::into_raw(arc.clone()); + let arc2 = unsafe { Arc::from_raw(ptr) }; + + assert_eq!(unsafe { &*ptr }, "foo"); + assert_eq!(arc, arc2); + + let arc: Arc = Arc::new(123); + + let ptr = Arc::into_raw(arc.clone()); + let arc2 = unsafe { Arc::from_raw(ptr) }; + + assert_eq!(unsafe { &*ptr }.to_string(), "123"); + assert_eq!(arc2.to_string(), "123"); + } + #[test] fn test_cowarc_clone_make_mut() { let mut cow0 = Arc::new(75); diff --git a/src/liballoc/macros.rs b/src/liballoc/macros.rs index 43ebaa4fbdb..c2a3019515f 100644 --- a/src/liballoc/macros.rs +++ b/src/liballoc/macros.rs @@ -105,22 +105,3 @@ macro_rules! vec { macro_rules! format { ($($arg:tt)*) => ($crate::fmt::format(format_args!($($arg)*))) } - -// Private macro to get the offset of a struct field in bytes from the address of the struct. -macro_rules! offset_of { - ($container:path, $field:ident) => {{ - // Make sure the field actually exists. This line ensures that a compile-time error is - // generated if $field is accessed through a Deref impl. - let $container { $field : _, .. }; - - // Create an (invalid) instance of the container and calculate the offset to its - // field. Using a null pointer might be UB if `&(*(0 as *const T)).field` is interpreted to - // be nullptr deref. - let invalid: $container = ::core::mem::uninitialized(); - let offset = &invalid.$field as *const _ as usize - &invalid as *const _ as usize; - - // Do not run destructors on the made up invalid instance. - ::core::mem::forget(invalid); - offset as isize - }}; -} diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 47f537caf31..f11e48d7cf4 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -252,7 +252,7 @@ use core::intrinsics::abort; use core::marker; use core::marker::Unsize; -use core::mem::{self, forget, size_of_val, uninitialized}; +use core::mem::{self, align_of_val, forget, size_of_val, uninitialized}; use core::ops::Deref; use core::ops::CoerceUnsized; use core::ptr::{self, Shared}; @@ -358,7 +358,9 @@ pub fn try_unwrap(this: Self) -> Result { Err(this) } } +} +impl Rc { /// Consumes the `Rc`, returning the wrapped pointer. /// /// To avoid a memory leak the pointer must be converted back to an `Rc` using @@ -412,17 +414,21 @@ pub fn into_raw(this: Self) -> *const T { /// ``` #[stable(feature = "rc_raw", since = "1.17.0")] pub unsafe fn from_raw(ptr: *const T) -> Self { - // To find the corresponding pointer to the `RcBox` we need to subtract the offset of the - // `value` field from the pointer. + // Align the unsized value to the end of the RcBox. + // Because it is ?Sized, it will always be the last field in memory. + let align = align_of_val(&*ptr); + let layout = Layout::new::>(); + let offset = (layout.size() + layout.padding_needed_for(align)) as isize; + + // Reverse the offset to find the original RcBox. + let fake_ptr = ptr as *mut RcBox; + let rc_ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)); - let ptr = (ptr as *const u8).offset(-offset_of!(RcBox, value)); Rc { - ptr: Shared::new_unchecked(ptr as *mut u8 as *mut _) + ptr: Shared::new_unchecked(rc_ptr), } } -} -impl Rc { /// Creates a new [`Weak`][weak] pointer to this value. /// /// [weak]: struct.Weak.html @@ -1481,6 +1487,28 @@ fn into_from_raw() { } } + #[test] + fn test_into_from_raw_unsized() { + use std::fmt::Display; + use std::string::ToString; + + let rc: Rc = Rc::from("foo"); + + let ptr = Rc::into_raw(rc.clone()); + let rc2 = unsafe { Rc::from_raw(ptr) }; + + assert_eq!(unsafe { &*ptr }, "foo"); + assert_eq!(rc, rc2); + + let rc: Rc = Rc::new(123); + + let ptr = Rc::into_raw(rc.clone()); + let rc2 = unsafe { Rc::from_raw(ptr) }; + + assert_eq!(unsafe { &*ptr }.to_string(), "123"); + assert_eq!(rc2.to_string(), "123"); + } + #[test] fn get_mut() { let mut x = Rc::new(3); -- 2.44.0