Skip to content

Commit bad1cda

Browse files
author
Julian Orth
committed
Add RcAlloc and ArcAlloc
These allocators use the StructAlloc infrastructure to add enough padding in front and behind the allocation so that Box<T, XAlloc> can be converted into X<T> without reallocating.
1 parent 5f42947 commit bad1cda

File tree

4 files changed

+173
-17
lines changed

4 files changed

+173
-17
lines changed

library/alloc/src/rc.rs

Lines changed: 76 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ use core::hash::{Hash, Hasher};
257257
use core::intrinsics::abort;
258258
use core::iter;
259259
use core::marker::{self, PhantomData, Unpin, Unsize};
260-
use core::mem::{self, forget, size_of_val};
260+
use core::mem::{self, forget, size_of_val, MaybeUninit};
261261
use core::ops::{CoerceUnsized, Deref, DispatchFromDyn, Receiver};
262262
use core::pin::Pin;
263263
use core::ptr::{self, NonNull};
@@ -853,13 +853,7 @@ impl<T: ?Sized> Rc<T> {
853853
/// ```
854854
#[stable(feature = "rc_raw", since = "1.17.0")]
855855
pub unsafe fn from_raw(ptr: *const T) -> Self {
856-
let offset = unsafe { data_offset(ptr) };
857-
858-
// Reverse the offset to find the original RcBox.
859-
let rc_ptr =
860-
unsafe { (ptr as *mut RcBox<T>).set_ptr_value((ptr as *mut u8).offset(-offset)) };
861-
862-
unsafe { Self::from_ptr(rc_ptr) }
856+
unsafe { Self::from_data_ptr(ptr).assume_init() }
863857
}
864858

865859
/// Creates a new [`Weak`] pointer to this allocation.
@@ -1217,6 +1211,35 @@ impl<T: ?Sized> Rc<T> {
12171211
Self::from_ptr(ptr)
12181212
}
12191213
}
1214+
1215+
/// # Safety
1216+
///
1217+
/// The caller must ensure that the pointer points to the `value` field of a `Global`
1218+
/// allocation of type `RcBox<T>`. Depending on how the pointer was created, the
1219+
/// `meta` field might or might not be uninitialized. It's up to the caller to ensure
1220+
/// that this field is set to the correct value before the return value is unwrapped.
1221+
#[inline]
1222+
unsafe fn from_data_ptr(ptr: *const T) -> MaybeUninit<Self> {
1223+
let offset = unsafe { data_offset(ptr) };
1224+
1225+
// Reverse the offset to find the original RcBox.
1226+
let rc_ptr =
1227+
unsafe { (ptr as *mut RcBox<T>).set_ptr_value((ptr as *mut u8).offset(-offset)) };
1228+
1229+
unsafe { MaybeUninit::new(Self::from_ptr(rc_ptr)) }
1230+
}
1231+
1232+
#[inline]
1233+
fn from_rc_alloc_box(v: Box<T, RcAlloc>) -> Rc<T> {
1234+
unsafe {
1235+
// SAFETY: RcAlloc allocations of `T` have the same layout as Global
1236+
// allocations of `RcBox<T>`. We use `Self::from_raw` as a shorthand
1237+
let data_ptr = Box::into_raw(v);
1238+
let mut rc = Self::from_data_ptr(data_ptr);
1239+
rc.assume_init_mut().ptr.as_mut().meta = RcBoxMetadata::new_strong();
1240+
rc.assume_init()
1241+
}
1242+
}
12201243
}
12211244

12221245
impl<T> Rc<[T]> {
@@ -2359,3 +2382,48 @@ unsafe fn data_offset<T: ?Sized>(data_ptr: *const T) -> isize {
23592382
RcStructAlloc::offset_of_data(data_layout) as isize
23602383
}
23612384
}
2385+
2386+
/// A memory allocator for [`Rc`] objects.
2387+
///
2388+
/// This allocator behaves like the underlying allocator except that values of type
2389+
/// [`Box<T, RcAlloc>`] can be converted to [`Rc<T>`] without copying the allocation.
2390+
///
2391+
/// # Example
2392+
///
2393+
/// ```
2394+
/// #![feature(struct_alloc, allocator_api)]
2395+
///
2396+
/// use std::rc::{Rc, RcAlloc};
2397+
///
2398+
/// let mut contents = Vec::new_in(RcAlloc::new());
2399+
/// contents.push(1u32);
2400+
/// let contents: Rc<[u32]> = contents.into_boxed_slice().into();
2401+
/// ```
2402+
#[derive(Debug)]
2403+
#[unstable(feature = "struct_alloc", issue = "none")]
2404+
pub struct RcAlloc<A = Global>(StructAlloc<RcBoxMetadata, A>);
2405+
2406+
#[unstable(feature = "struct_alloc", issue = "none")]
2407+
impl RcAlloc<Global> {
2408+
/// Constructs a new `RcAlloc<Global>`.
2409+
pub fn new() -> Self {
2410+
Self::new_with(Global)
2411+
}
2412+
}
2413+
2414+
#[unstable(feature = "struct_alloc", issue = "none")]
2415+
impl<A> RcAlloc<A> {
2416+
/// Constructs a new `RcAlloc<A>`.
2417+
pub fn new_with(alloc: A) -> Self {
2418+
RcAlloc(StructAlloc::new(alloc))
2419+
}
2420+
}
2421+
2422+
implement_struct_allocator!(RcAlloc);
2423+
2424+
#[unstable(feature = "struct_alloc", issue = "none")]
2425+
impl<T: ?Sized> From<Box<T, RcAlloc>> for Rc<T> {
2426+
fn from(v: Box<T, RcAlloc>) -> Self {
2427+
Self::from_rc_alloc_box(v)
2428+
}
2429+
}

library/alloc/src/rc/tests.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,3 +559,14 @@ fn test_rc_cyclic_with_two_ref() {
559559
assert_eq!(Rc::strong_count(&two_refs), 3);
560560
assert_eq!(Rc::weak_count(&two_refs), 2);
561561
}
562+
563+
#[test]
564+
fn test_from_rc_alloc() {
565+
let mut vec = Vec::new_in(RcAlloc::new());
566+
vec.push(1i32);
567+
let b = vec.into_boxed_slice();
568+
let b_addr = &*b as *const _;
569+
let rc = Rc::from(b);
570+
let rc_addr = &*rc as *const _;
571+
assert_eq!(b_addr, rc_addr);
572+
}

library/alloc/src/sync.rs

Lines changed: 75 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use core::hint;
1414
use core::intrinsics::abort;
1515
use core::iter;
1616
use core::marker::{PhantomData, Unpin, Unsize};
17-
use core::mem::{self, size_of_val};
17+
use core::mem::{self, size_of_val, MaybeUninit};
1818
use core::ops::{CoerceUnsized, Deref, DispatchFromDyn, Receiver};
1919
use core::pin::Pin;
2020
use core::ptr::{self, NonNull};
@@ -845,14 +845,7 @@ impl<T: ?Sized> Arc<T> {
845845
/// ```
846846
#[stable(feature = "rc_raw", since = "1.17.0")]
847847
pub unsafe fn from_raw(ptr: *const T) -> Self {
848-
unsafe {
849-
let offset = data_offset(ptr);
850-
851-
// Reverse the offset to find the original ArcInner.
852-
let arc_ptr = (ptr as *mut ArcInner<T>).set_ptr_value((ptr as *mut u8).offset(-offset));
853-
854-
Self::from_ptr(arc_ptr)
855-
}
848+
unsafe { Self::from_data_ptr(ptr).assume_init() }
856849
}
857850

858851
/// Creates a new [`Weak`] pointer to this allocation.
@@ -1155,6 +1148,34 @@ impl<T: ?Sized> Arc<T> {
11551148
Self::from_ptr(ptr)
11561149
}
11571150
}
1151+
1152+
/// # Safety
1153+
///
1154+
/// The caller must ensure that the pointer points to the `data` field of a `Global`
1155+
/// allocation of type `ArcInner<T>`. Depending on how the pointer was created, the
1156+
/// `meta` field might or might not be uninitialized. It's up to the caller to ensure
1157+
/// that this field is set to the correct value before the return value is unwrapped.
1158+
#[inline]
1159+
unsafe fn from_data_ptr(ptr: *const T) -> MaybeUninit<Self> {
1160+
unsafe {
1161+
let offset = data_offset(ptr);
1162+
1163+
// Reverse the offset to find the original ArcInner.
1164+
let arc_ptr = (ptr as *mut ArcInner<T>).set_ptr_value((ptr as *mut u8).offset(-offset));
1165+
1166+
MaybeUninit::new(Self::from_ptr(arc_ptr))
1167+
}
1168+
}
1169+
1170+
#[inline]
1171+
fn from_arc_alloc_box(v: Box<T, ArcAlloc>) -> Arc<T> {
1172+
unsafe {
1173+
let data_ptr = Box::into_raw(v);
1174+
let mut rc = Self::from_data_ptr(data_ptr);
1175+
rc.assume_init_mut().ptr.as_mut().meta = ArcInnerMetadata::new_strong();
1176+
rc.assume_init()
1177+
}
1178+
}
11581179
}
11591180

11601181
impl<T> Arc<[T]> {
@@ -2476,3 +2497,48 @@ unsafe fn data_offset<T: ?Sized>(data_ptr: *const T) -> isize {
24762497
ArcStructAlloc::offset_of_data(data_layout) as isize
24772498
}
24782499
}
2500+
2501+
/// A memory allocator for [`Arc`] objects.
2502+
///
2503+
/// This allocator behaves like the underlying allocator except that values of type
2504+
/// [`Box<T, ArcAlloc>`] can be converted to [`Arc<T>`] without copying the allocation.
2505+
///
2506+
/// # Example
2507+
///
2508+
/// ```
2509+
/// #![feature(struct_alloc, allocator_api)]
2510+
///
2511+
/// use alloc::sync::{Arc, ArcAlloc};
2512+
///
2513+
/// let mut contents = Vec::new_in(ArcAlloc::new());
2514+
/// contents.push(1u32);
2515+
/// let contents: Arc<[u32]> = contents.into_boxed_slice().into();
2516+
/// ```
2517+
#[derive(Debug)]
2518+
#[unstable(feature = "struct_alloc", issue = "none")]
2519+
pub struct ArcAlloc<A = Global>(StructAlloc<ArcInnerMetadata, A>);
2520+
2521+
#[unstable(feature = "struct_alloc", issue = "none")]
2522+
impl ArcAlloc<Global> {
2523+
/// Constructs a new `ArcAlloc<Global>`.
2524+
pub fn new() -> Self {
2525+
ArcAlloc(StructAlloc::new(Global))
2526+
}
2527+
}
2528+
2529+
#[unstable(feature = "struct_alloc", issue = "none")]
2530+
impl<A> ArcAlloc<A> {
2531+
/// Constructs a new `ArcAlloc<A>`.
2532+
pub fn new_with(alloc: A) -> Self {
2533+
ArcAlloc(StructAlloc::new(alloc))
2534+
}
2535+
}
2536+
2537+
implement_struct_allocator!(ArcAlloc);
2538+
2539+
#[unstable(feature = "struct_alloc", issue = "none")]
2540+
impl<T: ?Sized> From<Box<T, ArcAlloc>> for Arc<T> {
2541+
fn from(v: Box<T, ArcAlloc>) -> Self {
2542+
Self::from_arc_alloc_box(v)
2543+
}
2544+
}

library/alloc/src/sync/tests.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,3 +618,14 @@ fn test_arc_cyclic_two_refs() {
618618
assert_eq!(Arc::strong_count(&two_refs), 3);
619619
assert_eq!(Arc::weak_count(&two_refs), 2);
620620
}
621+
622+
#[test]
623+
fn test_from_arc_alloc() {
624+
let mut vec = Vec::new_in(ArcAlloc::new());
625+
vec.push(1i32);
626+
let b = vec.into_boxed_slice();
627+
let b_addr = &*b as *const _;
628+
let rc = Arc::from(b);
629+
let rc_addr = &*rc as *const _;
630+
assert_eq!(b_addr, rc_addr);
631+
}

0 commit comments

Comments
 (0)