Skip to content

Commit 487d757

Browse files
committed
rust: alloc: add some try_* methods we need
In preparation for enabling `no_global_oom_handling` for `alloc`, we need to add some new methods. They are all marked as: #[stable(feature = "kernel", since = "1.0.0")] for easy identification. Signed-off-by: Miguel Ojeda <[email protected]>
1 parent af999c4 commit 487d757

File tree

6 files changed

+599
-7
lines changed

6 files changed

+599
-7
lines changed

rust/alloc/raw_vec.rs

+51-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::collections::TryReserveError::{self, *};
2020
#[cfg(test)]
2121
mod tests;
2222

23-
#[cfg(not(no_global_oom_handling))]
23+
#[allow(dead_code)]
2424
enum AllocInit {
2525
/// The contents of the new memory are uninitialized.
2626
Uninitialized,
@@ -93,6 +93,16 @@ impl<T> RawVec<T, Global> {
9393
Self::with_capacity_in(capacity, Global)
9494
}
9595

96+
/// Tries to create a `RawVec` (on the system heap) with exactly the
97+
/// capacity and alignment requirements for a `[T; capacity]`. This is
98+
/// equivalent to calling `RawVec::new` when `capacity` is `0` or `T` is
99+
/// zero-sized. Note that if `T` is zero-sized this means you will
100+
/// *not* get a `RawVec` with the requested capacity.
101+
#[inline]
102+
pub fn try_with_capacity(capacity: usize) -> Result<Self, TryReserveError> {
103+
Self::try_with_capacity_in(capacity, Global)
104+
}
105+
96106
/// Like `with_capacity`, but guarantees the buffer is zeroed.
97107
#[cfg(not(no_global_oom_handling))]
98108
#[inline]
@@ -144,6 +154,13 @@ impl<T, A: Allocator> RawVec<T, A> {
144154
Self::allocate_in(capacity, AllocInit::Uninitialized, alloc)
145155
}
146156

157+
/// Like `try_with_capacity`, but parameterized over the choice of
158+
/// allocator for the returned `RawVec`.
159+
#[inline]
160+
pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result<Self, TryReserveError> {
161+
Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc)
162+
}
163+
147164
/// Like `with_capacity_zeroed`, but parameterized over the choice
148165
/// of allocator for the returned `RawVec`.
149166
#[cfg(not(no_global_oom_handling))]
@@ -218,6 +235,29 @@ impl<T, A: Allocator> RawVec<T, A> {
218235
}
219236
}
220237

238+
fn try_allocate_in(capacity: usize, init: AllocInit, alloc: A) -> Result<Self, TryReserveError> {
239+
if mem::size_of::<T>() == 0 {
240+
return Ok(Self::new_in(alloc));
241+
}
242+
243+
let layout = Layout::array::<T>(capacity)?;
244+
alloc_guard(layout.size())?;
245+
let result = match init {
246+
AllocInit::Uninitialized => alloc.allocate(layout),
247+
AllocInit::Zeroed => alloc.allocate_zeroed(layout),
248+
};
249+
let ptr = match result {
250+
Ok(ptr) => ptr,
251+
Err(_) => return Err(TryReserveError::AllocError { layout, non_exhaustive: () }),
252+
};
253+
254+
Ok(Self {
255+
ptr: unsafe { Unique::new_unchecked(ptr.cast().as_ptr()) },
256+
cap: Self::capacity_from_bytes(ptr.len()),
257+
alloc,
258+
})
259+
}
260+
221261
/// Reconstitutes a `RawVec` from a pointer, capacity, and allocator.
222262
///
223263
/// # Safety
@@ -394,6 +434,16 @@ impl<T, A: Allocator> RawVec<T, A> {
394434
pub fn shrink_to_fit(&mut self, amount: usize) {
395435
handle_reserve(self.shrink(amount));
396436
}
437+
438+
/// Tries to shrink the allocation down to the specified amount. If the given amount
439+
/// is 0, actually completely deallocates.
440+
///
441+
/// # Panics
442+
///
443+
/// Panics if the given amount is *larger* than the current capacity.
444+
pub fn try_shrink_to_fit(&mut self, amount: usize) -> Result<(), TryReserveError> {
445+
self.shrink(amount)
446+
}
397447
}
398448

399449
impl<T, A: Allocator> RawVec<T, A> {
@@ -465,7 +515,6 @@ impl<T, A: Allocator> RawVec<T, A> {
465515
Ok(())
466516
}
467517

468-
#[cfg(not(no_global_oom_handling))]
469518
fn shrink(&mut self, amount: usize) -> Result<(), TryReserveError> {
470519
assert!(amount <= self.capacity(), "Tried to shrink to a larger capacity");
471520

rust/alloc/slice.rs

+90-1
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,11 @@ use core::mem::size_of;
9595
use core::ptr;
9696

9797
use crate::alloc::Allocator;
98-
#[cfg(not(no_global_oom_handling))]
9998
use crate::alloc::Global;
10099
#[cfg(not(no_global_oom_handling))]
101100
use crate::borrow::ToOwned;
102101
use crate::boxed::Box;
102+
use crate::collections::TryReserveError;
103103
use crate::vec::Vec;
104104

105105
#[unstable(feature = "slice_range", issue = "76393")]
@@ -155,6 +155,7 @@ mod hack {
155155
use core::alloc::Allocator;
156156

157157
use crate::boxed::Box;
158+
use crate::collections::TryReserveError;
158159
use crate::vec::Vec;
159160

160161
// We shouldn't add inline attribute to this since this is used in
@@ -174,13 +175,24 @@ mod hack {
174175
T::to_vec(s, alloc)
175176
}
176177

178+
#[inline]
179+
pub fn try_to_vec<T: TryConvertVec, A: Allocator>(s: &[T], alloc: A) -> Result<Vec<T, A>, TryReserveError> {
180+
T::try_to_vec(s, alloc)
181+
}
182+
177183
#[cfg(not(no_global_oom_handling))]
178184
pub trait ConvertVec {
179185
fn to_vec<A: Allocator>(s: &[Self], alloc: A) -> Vec<Self, A>
180186
where
181187
Self: Sized;
182188
}
183189

190+
pub trait TryConvertVec {
191+
fn try_to_vec<A: Allocator>(s: &[Self], alloc: A) -> Result<Vec<Self, A>, TryReserveError>
192+
where
193+
Self: Sized;
194+
}
195+
184196
#[cfg(not(no_global_oom_handling))]
185197
impl<T: Clone> ConvertVec for T {
186198
#[inline]
@@ -233,6 +245,42 @@ mod hack {
233245
v
234246
}
235247
}
248+
249+
impl<T: Clone> TryConvertVec for T {
250+
#[inline]
251+
default fn try_to_vec<A: Allocator>(s: &[Self], alloc: A) -> Result<Vec<Self, A>, TryReserveError> {
252+
struct DropGuard<'a, T, A: Allocator> {
253+
vec: &'a mut Vec<T, A>,
254+
num_init: usize,
255+
}
256+
impl<'a, T, A: Allocator> Drop for DropGuard<'a, T, A> {
257+
#[inline]
258+
fn drop(&mut self) {
259+
// SAFETY:
260+
// items were marked initialized in the loop below
261+
unsafe {
262+
self.vec.set_len(self.num_init);
263+
}
264+
}
265+
}
266+
let mut vec = Vec::try_with_capacity_in(s.len(), alloc)?;
267+
let mut guard = DropGuard { vec: &mut vec, num_init: 0 };
268+
let slots = guard.vec.spare_capacity_mut();
269+
// .take(slots.len()) is necessary for LLVM to remove bounds checks
270+
// and has better codegen than zip.
271+
for (i, b) in s.iter().enumerate().take(slots.len()) {
272+
guard.num_init = i;
273+
slots[i].write(b.clone());
274+
}
275+
core::mem::forget(guard);
276+
// SAFETY:
277+
// the vec was allocated and initialized above to at least this length.
278+
unsafe {
279+
vec.set_len(s.len());
280+
}
281+
Ok(vec)
282+
}
283+
}
236284
}
237285

238286
#[lang = "slice_alloc"]
@@ -472,6 +520,24 @@ impl<T> [T] {
472520
self.to_vec_in(Global)
473521
}
474522

523+
/// Tries to copy `self` into a new `Vec`.
524+
///
525+
/// # Examples
526+
///
527+
/// ```
528+
/// let s = [10, 40, 30];
529+
/// let x = s.try_to_vec().unwrap();
530+
/// // Here, `s` and `x` can be modified independently.
531+
/// ```
532+
#[inline]
533+
#[stable(feature = "kernel", since = "1.0.0")]
534+
pub fn try_to_vec(&self) -> Result<Vec<T>, TryReserveError>
535+
where
536+
T: Clone,
537+
{
538+
self.try_to_vec_in(Global)
539+
}
540+
475541
/// Copies `self` into a new `Vec` with an allocator.
476542
///
477543
/// # Examples
@@ -496,6 +562,29 @@ impl<T> [T] {
496562
hack::to_vec(self, alloc)
497563
}
498564

565+
/// Tries to copy `self` into a new `Vec` with an allocator.
566+
///
567+
/// # Examples
568+
///
569+
/// ```
570+
/// #![feature(allocator_api)]
571+
///
572+
/// use std::alloc::System;
573+
///
574+
/// let s = [10, 40, 30];
575+
/// let x = s.try_to_vec_in(System).unwrap();
576+
/// // Here, `s` and `x` can be modified independently.
577+
/// ```
578+
#[inline]
579+
#[stable(feature = "kernel", since = "1.0.0")]
580+
pub fn try_to_vec_in<A: Allocator>(&self, alloc: A) -> Result<Vec<T, A>, TryReserveError>
581+
where
582+
T: Clone,
583+
{
584+
// N.B., see the `hack` module in this file for more details.
585+
hack::try_to_vec(self, alloc)
586+
}
587+
499588
/// Converts `self` into a vector without clones or allocation.
500589
///
501590
/// The resulting vector can be converted back into a box via

rust/alloc/str.rs

+17
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ use core::unicode::conversions;
3838

3939
use crate::borrow::ToOwned;
4040
use crate::boxed::Box;
41+
use crate::collections::TryReserveError;
4142
use crate::slice::{Concat, Join, SliceIndex};
4243
use crate::string::String;
4344
use crate::vec::Vec;
@@ -575,6 +576,22 @@ impl str {
575576
// make_ascii_lowercase() preserves the UTF-8 invariant.
576577
unsafe { String::from_utf8_unchecked(bytes) }
577578
}
579+
580+
/// Tries to create a `String`.
581+
///
582+
/// # Examples
583+
///
584+
/// Basic usage:
585+
///
586+
/// ```
587+
/// let s: &str = "a";
588+
/// let ss: String = s.try_to_owned().unwrap();
589+
/// ```
590+
#[inline]
591+
#[stable(feature = "kernel", since = "1.0.0")]
592+
pub fn try_to_owned(&self) -> Result<String, TryReserveError> {
593+
unsafe { Ok(String::from_utf8_unchecked(self.as_bytes().try_to_vec()?)) }
594+
}
578595
}
579596

580597
/// Converts a boxed slice of bytes to a boxed string slice without checking

rust/alloc/sync.rs

+52-1
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ use crate::alloc::{box_free, WriteCloneIntoRaw};
3636
use crate::alloc::{AllocError, Allocator, Global, Layout};
3737
use crate::borrow::{Cow, ToOwned};
3838
use crate::boxed::Box;
39+
use crate::collections::TryReserveError;
3940
use crate::rc::is_dangling;
4041
#[cfg(not(no_global_oom_handling))]
4142
use crate::string::String;
42-
#[cfg(not(no_global_oom_handling))]
4343
use crate::vec::Vec;
4444

4545
#[cfg(test)]
@@ -1188,6 +1188,18 @@ impl<T> Arc<[T]> {
11881188
}
11891189
}
11901190

1191+
/// Tries to allocate an `ArcInner<[T]>` with the given length.
1192+
unsafe fn try_allocate_for_slice(len: usize) -> Result<*mut ArcInner<[T]>, TryReserveError> {
1193+
unsafe {
1194+
let layout = Layout::array::<T>(len)?;
1195+
Self::try_allocate_for_layout(
1196+
layout,
1197+
|l| Global.allocate(l),
1198+
|mem| ptr::slice_from_raw_parts_mut(mem as *mut T, len) as *mut ArcInner<[T]>,
1199+
).map_err(|_| TryReserveError::AllocError { layout, non_exhaustive: () })
1200+
}
1201+
}
1202+
11911203
/// Copy elements from slice into newly allocated Arc<\[T\]>
11921204
///
11931205
/// Unsafe because the caller must either take ownership or bind `T: Copy`.
@@ -1202,6 +1214,19 @@ impl<T> Arc<[T]> {
12021214
}
12031215
}
12041216

1217+
/// Tries to copy elements from slice into newly allocated Arc<\[T\]>
1218+
///
1219+
/// Unsafe because the caller must either take ownership or bind `T: Copy`.
1220+
unsafe fn try_copy_from_slice(v: &[T]) -> Result<Arc<[T]>, TryReserveError> {
1221+
unsafe {
1222+
let ptr = Self::try_allocate_for_slice(v.len())?;
1223+
1224+
ptr::copy_nonoverlapping(v.as_ptr(), &mut (*ptr).data as *mut [T] as *mut T, v.len());
1225+
1226+
Ok(Self::from_ptr(ptr))
1227+
}
1228+
}
1229+
12051230
/// Constructs an `Arc<[T]>` from an iterator known to be of a certain size.
12061231
///
12071232
/// Behavior is undefined should the size be wrong.
@@ -2415,6 +2440,32 @@ impl<T> From<Vec<T>> for Arc<[T]> {
24152440
}
24162441
}
24172442

2443+
// Avoid `error: specializing impl repeats parameter` implementing `TryFrom`.
2444+
impl<T> Arc<[T]> {
2445+
/// Tries to allocate a reference-counted slice and move `v`'s items into it.
2446+
///
2447+
/// # Example
2448+
///
2449+
/// ```
2450+
/// # use std::sync::Arc;
2451+
/// let unique: Vec<i32> = vec![1, 2, 3];
2452+
/// let shared: Arc<[i32]> = Arc::try_from(unique).unwrap();
2453+
/// assert_eq!(&[1, 2, 3], &shared[..]);
2454+
/// ```
2455+
#[stable(feature = "kernel", since = "1.0.0")]
2456+
#[inline]
2457+
pub fn try_from_vec(mut v: Vec<T>) -> Result<Self, TryReserveError> {
2458+
unsafe {
2459+
let arc = Arc::try_copy_from_slice(&v)?;
2460+
2461+
// Allow the Vec to free its memory, but not destroy its contents
2462+
v.set_len(0);
2463+
2464+
Ok(arc)
2465+
}
2466+
}
2467+
}
2468+
24182469
#[stable(feature = "shared_from_cow", since = "1.45.0")]
24192470
impl<'a, B> From<Cow<'a, B>> for Arc<B>
24202471
where

0 commit comments

Comments
 (0)