about summary refs log tree commit diff
path: root/src/liballoc
diff options
context:
space:
mode:
authorCristi Cobzarenco <cristi.cobzarenco@gmail.com>2016-10-15 16:32:14 +0100
committerCristi Cobzarenco <cristi.cobzarenco@gmail.com>2016-11-05 00:50:41 +0000
commit651cf58f2e906fa6d333013891adca5074440bea (patch)
tree0a9de6025b1d0605d854beb2cbfbda06fb91c4d2 /src/liballoc
parentd34318dd538bf4c9175e4138b3e4188ea8211620 (diff)
downloadrust-651cf58f2e906fa6d333013891adca5074440bea.tar.gz
rust-651cf58f2e906fa6d333013891adca5074440bea.zip
Add `{into,from}_raw` to Rc and Arc
Diffstat (limited to 'src/liballoc')
-rw-r--r--src/liballoc/arc.rs79
-rw-r--r--src/liballoc/lib.rs4
-rw-r--r--src/liballoc/macros.rs28
-rw-r--r--src/liballoc/rc.rs79
4 files changed, 190 insertions, 0 deletions
diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs
index 7a07e007ce1..5db5a315f4a 100644
--- a/src/liballoc/arc.rs
+++ b/src/liballoc/arc.rs
@@ -270,6 +270,68 @@ impl<T> Arc<T> {
             Ok(elem)
         }
     }
+
+    /// Consumes the `Arc`, returning the wrapped pointer.
+    ///
+    /// To avoid a memory leak the pointer must be converted back to an `Arc` using
+    /// [`Arc::from_raw`][from_raw].
+    ///
+    /// [from_raw]: struct.Arc.html#method.from_raw
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(rc_raw)]
+    ///
+    /// use std::sync::Arc;
+    ///
+    /// let x = Arc::new(10);
+    /// let x_ptr = Arc::into_raw(x);
+    /// assert_eq!(unsafe { *x_ptr }, 10);
+    /// ```
+    #[unstable(feature = "rc_raw", issue = "37197")]
+    pub fn into_raw(this: Self) -> *mut T {
+        let ptr = unsafe { &mut (**this.ptr).data as *mut _ };
+        mem::forget(this);
+        ptr
+    }
+
+    /// Constructs an `Arc` from a raw pointer.
+    ///
+    /// The raw pointer must have been previously returned by a call to a
+    /// [`Arc::into_raw`][into_raw].
+    ///
+    /// This function is unsafe because improper use may lead to memory problems. For example, a
+    /// double-free may occur if the function is called twice on the same raw pointer.
+    ///
+    /// [into_raw]: struct.Arc.html#method.into_raw
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(rc_raw)]
+    ///
+    /// use std::sync::Arc;
+    ///
+    /// let x = Arc::new(10);
+    /// let x_ptr = Arc::into_raw(x);
+    ///
+    /// unsafe {
+    ///     // Convert back to an `Arc` to prevent leak.
+    ///     let x = Arc::from_raw(x_ptr);
+    ///     assert_eq!(*x, 10);
+    ///
+    ///     // Further calls to `Arc::from_raw(x_ptr)` would be memory unsafe.
+    /// }
+    ///
+    /// // The memory was freed when `x` went out of scope above, so `x_ptr` is now dangling!
+    /// ```
+    #[unstable(feature = "rc_raw", issue = "37197")]
+    pub unsafe fn from_raw(ptr: *mut T) -> Self {
+        // To find the corresponding pointer to the `ArcInner` we need to subtract the offset of the
+        // `data` field from the pointer.
+        Arc { ptr: Shared::new((ptr as *mut u8).offset(-offset_of!(ArcInner<T>, data)) as *mut _) }
+    }
 }
 
 impl<T: ?Sized> Arc<T> {
@@ -1180,6 +1242,23 @@ mod tests {
     }
 
     #[test]
+    fn into_from_raw() {
+        let x = Arc::new(box "hello");
+        let y = x.clone();
+
+        let x_ptr = Arc::into_raw(x);
+        drop(y);
+        unsafe {
+            assert_eq!(**x_ptr, "hello");
+
+            let x = Arc::from_raw(x_ptr);
+            assert_eq!(**x, "hello");
+
+            assert_eq!(Arc::try_unwrap(x).map(|x| *x), Ok("hello"));
+        }
+    }
+
+    #[test]
     fn test_cowarc_clone_make_mut() {
         let mut cow0 = Arc::new(75);
         let mut cow1 = cow0.clone();
diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs
index 31491106d97..0d450184ed8 100644
--- a/src/liballoc/lib.rs
+++ b/src/liballoc/lib.rs
@@ -99,6 +99,10 @@
 #[macro_use]
 extern crate std;
 
+// Module with internal macros used by other modules (needs to be included before other modules).
+#[macro_use]
+mod macros;
+
 // Heaps provided for low-level allocation strategies
 
 pub mod heap;
diff --git a/src/liballoc/macros.rs b/src/liballoc/macros.rs
new file mode 100644
index 00000000000..7da91c87e96
--- /dev/null
+++ b/src/liballoc/macros.rs
@@ -0,0 +1,28 @@
+// Copyright 2013-2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// 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 699f777138d..bd87abc720d 100644
--- a/src/liballoc/rc.rs
+++ b/src/liballoc/rc.rs
@@ -363,6 +363,68 @@ impl<T> Rc<T> {
     pub fn would_unwrap(this: &Self) -> bool {
         Rc::strong_count(&this) == 1
     }
+
+    /// Consumes the `Rc`, returning the wrapped pointer.
+    ///
+    /// To avoid a memory leak the pointer must be converted back to an `Rc` using
+    /// [`Rc::from_raw`][from_raw].
+    ///
+    /// [from_raw]: struct.Rc.html#method.from_raw
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(rc_raw)]
+    ///
+    /// use std::rc::Rc;
+    ///
+    /// let x = Rc::new(10);
+    /// let x_ptr = Rc::into_raw(x);
+    /// assert_eq!(unsafe { *x_ptr }, 10);
+    /// ```
+    #[unstable(feature = "rc_raw", issue = "37197")]
+    pub fn into_raw(this: Self) -> *mut T {
+        let ptr = unsafe { &mut (**this.ptr).value as *mut _ };
+        mem::forget(this);
+        ptr
+    }
+
+    /// Constructs an `Rc` from a raw pointer.
+    ///
+    /// The raw pointer must have been previously returned by a call to a
+    /// [`Rc::into_raw`][into_raw].
+    ///
+    /// This function is unsafe because improper use may lead to memory problems. For example, a
+    /// double-free may occur if the function is called twice on the same raw pointer.
+    ///
+    /// [into_raw]: struct.Rc.html#method.into_raw
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(rc_raw)]
+    ///
+    /// use std::rc::Rc;
+    ///
+    /// let x = Rc::new(10);
+    /// let x_ptr = Rc::into_raw(x);
+    ///
+    /// unsafe {
+    ///     // Convert back to an `Rc` to prevent leak.
+    ///     let x = Rc::from_raw(x_ptr);
+    ///     assert_eq!(*x, 10);
+    ///
+    ///     // Further calls to `Rc::from_raw(x_ptr)` would be memory unsafe.
+    /// }
+    ///
+    /// // The memory was freed when `x` went out of scope above, so `x_ptr` is now dangling!
+    /// ```
+    #[unstable(feature = "rc_raw", issue = "37197")]
+    pub unsafe fn from_raw(ptr: *mut T) -> Self {
+        // To find the corresponding pointer to the `RcBox` we need to subtract the offset of the
+        // `value` field from the pointer.
+        Rc { ptr: Shared::new((ptr as *mut u8).offset(-offset_of!(RcBox<T>, value)) as *mut _) }
+    }
 }
 
 impl<T: ?Sized> Rc<T> {
@@ -1262,6 +1324,23 @@ mod tests {
     }
 
     #[test]
+    fn into_from_raw() {
+        let x = Rc::new(box "hello");
+        let y = x.clone();
+
+        let x_ptr = Rc::into_raw(x);
+        drop(y);
+        unsafe {
+            assert_eq!(**x_ptr, "hello");
+
+            let x = Rc::from_raw(x_ptr);
+            assert_eq!(**x, "hello");
+
+            assert_eq!(Rc::try_unwrap(x).map(|x| *x), Ok("hello"));
+        }
+    }
+
+    #[test]
     fn get_mut() {
         let mut x = Rc::new(3);
         *Rc::get_mut(&mut x).unwrap() = 4;