Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

alloc: add some try_* methods Rust-for-Linux needs #91559

Closed
wants to merge 9 commits into from
Closed
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
20 changes: 8 additions & 12 deletions library/alloc/src/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -626,12 +626,10 @@ impl<T> Box<[T]> {
#[inline]
pub fn try_new_uninit_slice(len: usize) -> Result<Box<[mem::MaybeUninit<T>]>, AllocError> {
unsafe {
let layout = match Layout::array::<mem::MaybeUninit<T>>(len) {
Ok(l) => l,
Err(_) => return Err(AllocError),
};
let ptr = Global.allocate(layout)?;
Ok(RawVec::from_raw_parts_in(ptr.as_mut_ptr() as *mut _, len, Global).into_box(len))
match RawVec::try_with_capacity(len) {
Ok(raw) => Ok(raw.into_box(len)),
Err(_) => Err(AllocError),
}
}
}

Expand All @@ -658,12 +656,10 @@ impl<T> Box<[T]> {
#[inline]
pub fn try_new_zeroed_slice(len: usize) -> Result<Box<[mem::MaybeUninit<T>]>, AllocError> {
unsafe {
let layout = match Layout::array::<mem::MaybeUninit<T>>(len) {
Ok(l) => l,
Err(_) => return Err(AllocError),
};
let ptr = Global.allocate_zeroed(layout)?;
Ok(RawVec::from_raw_parts_in(ptr.as_mut_ptr() as *mut _, len, Global).into_box(len))
match RawVec::try_with_capacity_zeroed(len) {
Ok(raw) => Ok(raw.into_box(len)),
Err(_) => Err(AllocError),
}
}
}
}
Expand Down
17 changes: 17 additions & 0 deletions library/alloc/src/collections/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@ pub use linked_list::LinkedList;
#[doc(no_inline)]
pub use vec_deque::VecDeque;

#[cfg(not(no_global_oom_handling))]
use crate::alloc::handle_alloc_error;
use crate::alloc::{Layout, LayoutError};
#[cfg(not(no_global_oom_handling))]
use crate::raw_vec::capacity_overflow;
use core::fmt::Display;

/// The error type for `try_reserve` methods.
Expand Down Expand Up @@ -106,6 +110,19 @@ pub enum TryReserveErrorKind {
},
}

#[cfg(not(no_global_oom_handling))]
impl TryReserveError {
/// Panic, or worse, according to the global handlers for each case.
pub(crate) fn handle(self) -> ! {
match self.kind {
TryReserveErrorKind::CapacityOverflow => capacity_overflow(),
TryReserveErrorKind::AllocError { layout, non_exhaustive: () } => {
handle_alloc_error(layout)
}
}
}
}

#[unstable(
feature = "try_reserve_kind",
reason = "Uncertain how much info should be exposed",
Expand Down
1 change: 1 addition & 0 deletions library/alloc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
#![feature(iter_advance_by)]
#![feature(iter_zip)]
#![feature(layout_for_ptr)]
#![feature(more_fallible_allocation_methods)]
#![feature(maybe_uninit_extra)]
#![feature(maybe_uninit_slice)]
#![cfg_attr(test, feature(new_uninit))]
Expand Down
106 changes: 79 additions & 27 deletions library/alloc/src/raw_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,33 @@ impl<T> RawVec<T, Global> {
Self::with_capacity_in(capacity, Global)
}

/// Tries to create a `RawVec` (on the system heap) with exactly the
/// capacity and alignment requirements for a `[T; capacity]`. This is
/// equivalent to calling `RawVec::new` when `capacity` is `0` or `T` is
/// zero-sized. Note that if `T` is zero-sized this means you will
/// *not* get a `RawVec` with the requested capacity.
#[cfg(not(any(no_global_oom_handling, test)))]
#[must_use]
#[inline]
pub fn try_with_capacity(capacity: usize) -> Result<Self, TryReserveError> {
Self::try_with_capacity_in(capacity, Global)
}

/// Like `with_capacity`, but guarantees the buffer is zeroed.
#[cfg(not(any(no_global_oom_handling, test)))]
#[must_use]
#[inline]
pub fn with_capacity_zeroed(capacity: usize) -> Self {
Self::with_capacity_zeroed_in(capacity, Global)
}

/// Like `try_with_capacity`, but guarantees a successfully allocated buffer is zeroed.
#[cfg(not(any(no_global_oom_handling, test)))]
#[must_use]
#[inline]
pub fn try_with_capacity_zeroed(capacity: usize) -> Result<Self, TryReserveError> {
Self::try_with_capacity_zeroed_in(capacity, Global)
}
}

impl<T, A: Allocator> RawVec<T, A> {
Expand Down Expand Up @@ -132,6 +152,13 @@ impl<T, A: Allocator> RawVec<T, A> {
Self::allocate_in(capacity, AllocInit::Uninitialized, alloc)
}

/// Like `try_with_capacity`, but parameterized over the choice of
/// allocator for the returned `RawVec`.
#[inline]
pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result<Self, TryReserveError> {
Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc)
}

/// Like `with_capacity_zeroed`, but parameterized over the choice
/// of allocator for the returned `RawVec`.
#[cfg(not(no_global_oom_handling))]
Expand All @@ -140,6 +167,15 @@ impl<T, A: Allocator> RawVec<T, A> {
Self::allocate_in(capacity, AllocInit::Zeroed, alloc)
}

/// Like `with_capacity_zeroed`, but parameterized over the choice
/// of allocator for the returned `RawVec`.
#[cfg(not(any(no_global_oom_handling, test)))]
#[must_use]
#[inline]
pub fn try_with_capacity_zeroed_in(capacity: usize, alloc: A) -> Result<Self, TryReserveError> {
Self::try_allocate_in(capacity, AllocInit::Zeroed, alloc)
}

/// Converts the entire buffer into `Box<[MaybeUninit<T>]>` with the specified `len`.
///
/// Note that this will correctly reconstitute any `cap` changes
Expand Down Expand Up @@ -168,33 +204,43 @@ impl<T, A: Allocator> RawVec<T, A> {

#[cfg(not(no_global_oom_handling))]
fn allocate_in(capacity: usize, init: AllocInit, alloc: A) -> Self {
match Self::try_allocate_in(capacity, init, alloc) {
Ok(r) => r,
Err(e) => e.handle(),
}
}

fn try_allocate_in(
capacity: usize,
init: AllocInit,
alloc: A,
) -> Result<Self, TryReserveError> {
if mem::size_of::<T>() == 0 {
Self::new_in(alloc)
Ok(Self::new_in(alloc))
} else {
// We avoid `unwrap_or_else` here because it bloats the amount of
// LLVM IR generated.
let layout = match Layout::array::<T>(capacity) {
Ok(layout) => layout,
Err(_) => capacity_overflow(),
Err(_) => return Err(TryReserveError::from(CapacityOverflow)),
};
match alloc_guard(layout.size()) {
Ok(_) => {}
Err(_) => capacity_overflow(),
}
alloc_guard(layout.size())?;
let result = match init {
AllocInit::Uninitialized => alloc.allocate(layout),
AllocInit::Zeroed => alloc.allocate_zeroed(layout),
};
let ptr = match result {
Ok(ptr) => ptr,
Err(_) => handle_alloc_error(layout),
Err(_) => {
return Err(TryReserveError::from(AllocError { layout, non_exhaustive: () }));
}
};

Self {
Ok(Self {
ptr: unsafe { Unique::new_unchecked(ptr.cast().as_ptr()) },
cap: Self::capacity_from_bytes(ptr.len()),
alloc,
}
})
}
}

Expand Down Expand Up @@ -349,7 +395,29 @@ impl<T, A: Allocator> RawVec<T, A> {
/// Aborts on OOM.
#[cfg(not(no_global_oom_handling))]
pub fn shrink_to_fit(&mut self, amount: usize) {
handle_reserve(self.shrink(amount));
handle_reserve(self.try_shrink_to_fit(amount));
}

/// Tries to shrink the allocation down to the specified amount. If the given amount
/// is 0, actually completely deallocates.
///
/// # Panics
///
/// Panics if the given amount is *larger* than the current capacity.
pub fn try_shrink_to_fit(&mut self, amount: usize) -> Result<(), TryReserveError> {
assert!(amount <= self.capacity(), "Tried to shrink to a larger capacity");

let (ptr, layout) = if let Some(mem) = self.current_memory() { mem } else { return Ok(()) };
let new_size = amount * mem::size_of::<T>();

let ptr = unsafe {
let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
self.alloc
.shrink(ptr, layout, new_layout)
.map_err(|_| AllocError { layout: new_layout, non_exhaustive: () })?
};
self.set_ptr(ptr);
Ok(())
}
}

Expand Down Expand Up @@ -421,22 +489,6 @@ impl<T, A: Allocator> RawVec<T, A> {
self.set_ptr(ptr);
Ok(())
}

fn shrink(&mut self, amount: usize) -> Result<(), TryReserveError> {
assert!(amount <= self.capacity(), "Tried to shrink to a larger capacity");

let (ptr, layout) = if let Some(mem) = self.current_memory() { mem } else { return Ok(()) };
let new_size = amount * mem::size_of::<T>();

let ptr = unsafe {
let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
self.alloc
.shrink(ptr, layout, new_layout)
.map_err(|_| AllocError { layout: new_layout, non_exhaustive: () })?
};
self.set_ptr(ptr);
Ok(())
}
}

// This function is outside `RawVec` to minimize compile times. See the comment
Expand Down Expand Up @@ -513,6 +565,6 @@ fn alloc_guard(alloc_size: usize) -> Result<(), TryReserveError> {
// ensure that the code generation related to these panics is minimal as there's
// only one location which panics rather than a bunch throughout the module.
#[cfg(not(no_global_oom_handling))]
fn capacity_overflow() -> ! {
pub fn capacity_overflow() -> ! {
panic!("capacity overflow");
}
Loading