Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions library/alloc/src/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,128 @@ impl<T, A: Allocator> Box<T, A> {
}
}

impl<T: ?Sized + CloneToUninit> Box<T> {
/// Allocates memory on the heap then clones `src` into it.
///
/// This doesn't actually allocate if `src` is zero-sized.
///
/// # Examples
///
/// ```
/// #![feature(clone_from_ref)]
///
/// let hello: Box<str> = Box::clone_from_ref("hello");
/// ```
#[cfg(not(no_global_oom_handling))]
#[unstable(feature = "clone_from_ref", issue = "149075")]
#[must_use]
#[inline]
pub fn clone_from_ref(src: &T) -> Box<T> {
Box::clone_from_ref_in(src, Global)
}

/// Allocates memory on the heap then clones `src` into it, returning an error if allocation fails.
///
/// This doesn't actually allocate if `src` is zero-sized.
///
/// # Examples
///
/// ```
/// #![feature(clone_from_ref)]
/// #![feature(allocator_api)]
///
/// let hello: Box<str> = Box::try_clone_from_ref("hello")?;
/// # Ok::<(), std::alloc::AllocError>(())
/// ```
#[unstable(feature = "clone_from_ref", issue = "149075")]
//#[unstable(feature = "allocator_api", issue = "32838")]
#[must_use]
#[inline]
pub fn try_clone_from_ref(src: &T) -> Result<Box<T>, AllocError> {
Box::try_clone_from_ref_in(src, Global)
}
}

impl<T: ?Sized + CloneToUninit, A: Allocator> Box<T, A> {
/// Allocates memory in the given allocator then clones `src` into it.
///
/// This doesn't actually allocate if `src` is zero-sized.
///
/// # Examples
///
/// ```
/// #![feature(clone_from_ref)]
/// #![feature(allocator_api)]
///
/// use std::alloc::System;
///
/// let hello: Box<str, System> = Box::clone_from_ref_in("hello", System);
/// ```
#[cfg(not(no_global_oom_handling))]
#[unstable(feature = "clone_from_ref", issue = "149075")]
//#[unstable(feature = "allocator_api", issue = "32838")]
#[must_use]
#[inline]
pub fn clone_from_ref_in(src: &T, alloc: A) -> Box<T, A> {
let layout = Layout::for_value::<T>(src);
match Box::try_clone_from_ref_in(src, alloc) {
Ok(bx) => bx,
Err(_) => handle_alloc_error(layout),
}
}

/// Allocates memory in the given allocator then clones `src` into it, returning an error if allocation fails.
///
/// This doesn't actually allocate if `src` is zero-sized.
///
/// # Examples
///
/// ```
/// #![feature(clone_from_ref)]
/// #![feature(allocator_api)]
///
/// use std::alloc::System;
///
/// let hello: Box<str, System> = Box::try_clone_from_ref_in("hello", System)?;
/// # Ok::<(), std::alloc::AllocError>(())
/// ```
#[unstable(feature = "clone_from_ref", issue = "149075")]
//#[unstable(feature = "allocator_api", issue = "32838")]
#[must_use]
#[inline]
pub fn try_clone_from_ref_in(src: &T, alloc: A) -> Result<Box<T, A>, AllocError> {
struct DeallocDropGuard<'a, A: Allocator>(Layout, &'a A, NonNull<u8>);
impl<'a, A: Allocator> Drop for DeallocDropGuard<'a, A> {
fn drop(&mut self) {
let &mut DeallocDropGuard(layout, alloc, ptr) = self;
// Safety: `ptr` was allocated by `*alloc` with layout `layout`
unsafe {
alloc.deallocate(ptr, layout);
}
}
}
let layout = Layout::for_value::<T>(src);
let (ptr, guard) = if layout.size() == 0 {
(layout.dangling(), None)
} else {
// Safety: layout is non-zero-sized
let ptr = alloc.allocate(layout)?.cast();
(ptr, Some(DeallocDropGuard(layout, &alloc, ptr)))
};
let ptr = ptr.as_ptr();
// Safety: `*ptr` is newly allocated, correctly aligned to `align_of_val(src)`,
// and is valid for writes for `size_of_val(src)`.
// If this panics, then `guard` will deallocate for us (if allocation occuured)
unsafe {
<T as CloneToUninit>::clone_to_uninit(src, ptr);
}
// Defuse the deallocate guard
core::mem::forget(guard);
// Safety: We just initialized `*ptr` as a clone of `src`
Ok(unsafe { Box::from_raw_in(ptr.with_metadata_of(src), alloc) })
}
}

impl<T> Box<[T]> {
/// Constructs a new boxed slice with uninitialized contents.
///
Expand Down
43 changes: 4 additions & 39 deletions library/alloc/src/boxed/convert.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
use core::any::Any;
#[cfg(not(no_global_oom_handling))]
use core::clone::TrivialClone;
use core::error::Error;
#[cfg(not(no_global_oom_handling))]
use core::fmt;
use core::mem;
use core::pin::Pin;
#[cfg(not(no_global_oom_handling))]
use core::{fmt, ptr};

use crate::alloc::Allocator;
#[cfg(not(no_global_oom_handling))]
use crate::borrow::Cow;
use crate::boxed::Box;
#[cfg(not(no_global_oom_handling))]
use crate::raw_vec::RawVec;
#[cfg(not(no_global_oom_handling))]
use crate::str::from_boxed_utf8_unchecked;
#[cfg(not(no_global_oom_handling))]
use crate::string::String;
#[cfg(not(no_global_oom_handling))]
use crate::vec::Vec;
Expand Down Expand Up @@ -62,35 +56,6 @@ where
}
}

/// Specialization trait used for `From<&[T]>`.
#[cfg(not(no_global_oom_handling))]
trait BoxFromSlice<T> {
fn from_slice(slice: &[T]) -> Self;
}

#[cfg(not(no_global_oom_handling))]
impl<T: Clone> BoxFromSlice<T> for Box<[T]> {
#[inline]
default fn from_slice(slice: &[T]) -> Self {
slice.to_vec().into_boxed_slice()
}
}

#[cfg(not(no_global_oom_handling))]
impl<T: TrivialClone> BoxFromSlice<T> for Box<[T]> {
#[inline]
fn from_slice(slice: &[T]) -> Self {
let len = slice.len();
let buf = RawVec::with_capacity(len);
// SAFETY: since `T` implements `TrivialClone`, this is sound and
// equivalent to the above.
unsafe {
ptr::copy_nonoverlapping(slice.as_ptr(), buf.ptr(), len);
buf.into_box(slice.len()).assume_init()
}
}
}

#[cfg(not(no_global_oom_handling))]
#[stable(feature = "box_from_slice", since = "1.17.0")]
impl<T: Clone> From<&[T]> for Box<[T]> {
Expand All @@ -109,7 +74,7 @@ impl<T: Clone> From<&[T]> for Box<[T]> {
/// ```
#[inline]
fn from(slice: &[T]) -> Box<[T]> {
<Self as BoxFromSlice<T>>::from_slice(slice)
Box::clone_from_ref(slice)
}
}

Expand Down Expand Up @@ -170,7 +135,7 @@ impl From<&str> for Box<str> {
/// ```
#[inline]
fn from(s: &str) -> Box<str> {
unsafe { from_boxed_utf8_unchecked(Box::from(s.as_bytes())) }
Box::clone_from_ref(s)
}
}

Expand Down
3 changes: 1 addition & 2 deletions library/alloc/src/ffi/c_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -766,8 +766,7 @@ impl From<&CStr> for Box<CStr> {
/// Converts a `&CStr` into a `Box<CStr>`,
/// by copying the contents into a newly allocated [`Box`].
fn from(s: &CStr) -> Box<CStr> {
let boxed: Box<[u8]> = Box::from(s.to_bytes_with_nul());
unsafe { Box::from_raw(Box::into_raw(boxed) as *mut CStr) }
Box::clone_from_ref(s)
}
}

Expand Down
129 changes: 113 additions & 16 deletions library/alloc/src/rc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1290,6 +1290,104 @@ impl<T, A: Allocator> Rc<mem::MaybeUninit<T>, A> {
}
}

impl<T: ?Sized + CloneToUninit> Rc<T> {
/// Constructs a new `Rc<T>` with a clone of `value`.
///
/// # Examples
///
/// ```
/// #![feature(clone_from_ref)]
/// use std::rc::Rc;
///
/// let hello: Rc<str> = Rc::clone_from_ref("hello");
/// ```
#[cfg(not(no_global_oom_handling))]
#[unstable(feature = "clone_from_ref", issue = "149075")]
pub fn clone_from_ref(value: &T) -> Rc<T> {
Rc::clone_from_ref_in(value, Global)
}

/// Constructs a new `Rc<T>` with a clone of `value`, returning an error if allocation fails
///
/// # Examples
///
/// ```
/// #![feature(clone_from_ref)]
/// #![feature(allocator_api)]
/// use std::rc::Rc;
///
/// let hello: Rc<str> = Rc::try_clone_from_ref("hello")?;
/// # Ok::<(), std::alloc::AllocError>(())
/// ```
#[unstable(feature = "clone_from_ref", issue = "149075")]
//#[unstable(feature = "allocator_api", issue = "32838")]
pub fn try_clone_from_ref(value: &T) -> Result<Rc<T>, AllocError> {
Rc::try_clone_from_ref_in(value, Global)
}
}

impl<T: ?Sized + CloneToUninit, A: Allocator> Rc<T, A> {
/// Constructs a new `Rc<T>` with a clone of `value` in the provided allocator.
///
/// # Examples
///
/// ```
/// #![feature(clone_from_ref)]
/// #![feature(allocator_api)]
/// use std::rc::Rc;
/// use std::alloc::System;
///
/// let hello: Rc<str, System> = Rc::clone_from_ref_in("hello", System);
/// ```
#[cfg(not(no_global_oom_handling))]
#[unstable(feature = "clone_from_ref", issue = "149075")]
//#[unstable(feature = "allocator_api", issue = "32838")]
pub fn clone_from_ref_in(value: &T, alloc: A) -> Rc<T, A> {
// `in_progress` drops the allocation if we panic before finishing initializing it.
let mut in_progress: UniqueRcUninit<T, A> = UniqueRcUninit::new(value, alloc);

// Initialize with clone of value.
let initialized_clone = unsafe {
// Clone. If the clone panics, `in_progress` will be dropped and clean up.
value.clone_to_uninit(in_progress.data_ptr().cast());
// Cast type of pointer, now that it is initialized.
in_progress.into_rc()
};

initialized_clone
}

/// Constructs a new `Rc<T>` with a clone of `value` in the provided allocator, returning an error if allocation fails
///
/// # Examples
///
/// ```
/// #![feature(clone_from_ref)]
/// #![feature(allocator_api)]
/// use std::rc::Rc;
/// use std::alloc::System;
///
/// let hello: Rc<str, System> = Rc::try_clone_from_ref_in("hello", System)?;
/// # Ok::<(), std::alloc::AllocError>(())
/// ```
#[unstable(feature = "clone_from_ref", issue = "149075")]
//#[unstable(feature = "allocator_api", issue = "32838")]
pub fn try_clone_from_ref_in(value: &T, alloc: A) -> Result<Rc<T, A>, AllocError> {
// `in_progress` drops the allocation if we panic before finishing initializing it.
let mut in_progress: UniqueRcUninit<T, A> = UniqueRcUninit::try_new(value, alloc)?;

// Initialize with clone of value.
let initialized_clone = unsafe {
// Clone. If the clone panics, `in_progress` will be dropped and clean up.
value.clone_to_uninit(in_progress.data_ptr().cast());
// Cast type of pointer, now that it is initialized.
in_progress.into_rc()
};

Ok(initialized_clone)
}
}

impl<T, A: Allocator> Rc<[mem::MaybeUninit<T>], A> {
/// Converts to `Rc<[T]>`.
///
Expand Down Expand Up @@ -1969,22 +2067,7 @@ impl<T: ?Sized + CloneToUninit, A: Allocator + Clone> Rc<T, A> {

if Rc::strong_count(this) != 1 {
// Gotta clone the data, there are other Rcs.

let this_data_ref: &T = &**this;
// `in_progress` drops the allocation if we panic before finishing initializing it.
let mut in_progress: UniqueRcUninit<T, A> =
UniqueRcUninit::new(this_data_ref, this.alloc.clone());

// Initialize with clone of this.
let initialized_clone = unsafe {
// Clone. If the clone panics, `in_progress` will be dropped and clean up.
this_data_ref.clone_to_uninit(in_progress.data_ptr().cast());
// Cast type of pointer, now that it is initialized.
in_progress.into_rc()
};

// Replace `this` with newly constructed Rc.
*this = initialized_clone;
*this = Rc::clone_from_ref_in(&**this, this.alloc.clone());
} else if Rc::weak_count(this) != 0 {
// Can just steal the data, all that's left is Weaks

Expand Down Expand Up @@ -4380,6 +4463,20 @@ impl<T: ?Sized, A: Allocator> UniqueRcUninit<T, A> {
Self { ptr: NonNull::new(ptr).unwrap(), layout_for_value: layout, alloc: Some(alloc) }
}

/// Allocates a RcInner with layout suitable to contain `for_value` or a clone of it,
/// returning an error if allocation fails.
fn try_new(for_value: &T, alloc: A) -> Result<UniqueRcUninit<T, A>, AllocError> {
let layout = Layout::for_value(for_value);
let ptr = unsafe {
Rc::try_allocate_for_layout(
layout,
|layout_for_rc_inner| alloc.allocate(layout_for_rc_inner),
|mem| mem.with_metadata_of(ptr::from_ref(for_value) as *const RcInner<T>),
)?
};
Ok(Self { ptr: NonNull::new(ptr).unwrap(), layout_for_value: layout, alloc: Some(alloc) })
}

/// Returns the pointer to be written into to initialize the [`Rc`].
fn data_ptr(&mut self) -> *mut T {
let offset = data_offset_align(self.layout_for_value.align());
Expand Down
Loading
Loading