Skip to content

Commit 6b0a5e9

Browse files
committed
rust: add fallible allocation methods
These are all fallible alternatives to all currently used methods that may perform infallible allocation. `assume_fallible` is a semantically marker function to denote that we have proved that the closure passed to it will not perform infallible allocation. Signed-off-by: Gary Guo <[email protected]>
1 parent 7884043 commit 6b0a5e9

File tree

5 files changed

+120
-8
lines changed

5 files changed

+120
-8
lines changed

rust/kernel/io_buffer.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
//! Buffers used in IO.
44
5+
use crate::traits::VecExt;
56
use crate::Result;
67
use alloc::vec::Vec;
78
use core::mem::{size_of, MaybeUninit};
@@ -29,9 +30,8 @@ pub trait IoBufferReader {
2930
///
3031
/// Returns `EFAULT` if the address does not currently point to mapped, readable memory.
3132
fn read_all(&mut self) -> Result<Vec<u8>> {
32-
let mut data = Vec::<u8>::new();
33-
data.try_reserve_exact(self.len())?;
34-
data.resize(self.len(), 0);
33+
let mut data = Vec::try_with_capacity(self.len())?;
34+
data.try_resize(self.len(), 0)?;
3535

3636
// SAFETY: The output buffer is valid as we just allocated it.
3737
unsafe { self.read_raw(data.as_mut_ptr(), data.len())? };

rust/kernel/module_param.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
//! C header: [`include/linux/moduleparam.h`](../../../include/linux/moduleparam.h)
66
77
use crate::str::CStr;
8+
use crate::traits::TryToOwned;
89
use core::fmt::Write;
910

1011
/// Types that can be used for module parameters.
@@ -475,9 +476,7 @@ impl ModuleParam for StringParam {
475476
let slab_available = unsafe { crate::bindings::slab_is_available() };
476477
arg.and_then(|arg| {
477478
if slab_available {
478-
let mut vec = alloc::vec::Vec::new();
479-
vec.try_reserve_exact(arg.len()).ok()?;
480-
vec.extend_from_slice(arg);
479+
let vec = arg.try_to_owned().ok()?;
481480
Some(StringParam::Owned(vec))
482481
} else {
483482
Some(StringParam::Ref(arg))

rust/kernel/prelude.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,4 @@ pub use super::static_assert;
2323

2424
pub use super::{KernelModule, Result};
2525

26-
pub use crate::traits::TryPin;
26+
pub use crate::traits::{TryPin, TryToOwned, VecExt as _};

rust/kernel/traits.rs

+113
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,19 @@
44
55
use core::{ops::Deref, pin::Pin};
66

7+
use alloc::collections::TryReserveError;
8+
use alloc::string::String;
9+
use alloc::vec::Vec;
710
use alloc::{alloc::AllocError, sync::Arc};
811

12+
type AllocResult<T = ()> = core::result::Result<T, AllocError>;
13+
type CollectionResult<T = ()> = core::result::Result<T, TryReserveError>;
14+
15+
#[inline]
16+
fn assume_fallible<T, F: FnOnce() -> T>(f: F) -> T {
17+
f()
18+
}
19+
920
/// Trait which provides a fallible version of `pin()` for pointer types.
1021
///
1122
/// Common pointer types which implement a `pin()` method include [`Box`](alloc::boxed::Box) and [`Arc`].
@@ -24,3 +35,105 @@ impl<T> TryPin<Arc<T>> for Arc<T> {
2435
Ok(unsafe { Pin::new_unchecked(Arc::try_new(data)?) })
2536
}
2637
}
38+
39+
/// Faillible alternative to [`alloc::borrow::ToOwned`].
40+
pub trait TryToOwned {
41+
/// The resulting type after obtaining ownership.
42+
type Owned: core::borrow::Borrow<Self>;
43+
44+
/// Faillible alternative to [`alloc::borrow::ToOwned::to_owned`].
45+
#[must_use = "cloning is often expensive and is not expected to have side effects"]
46+
fn try_to_owned(&self) -> AllocResult<Self::Owned>;
47+
48+
/// Faillible alternative to [`alloc::borrow::ToOwned::clone_into`].
49+
fn try_clone_into(&self, target: &mut Self::Owned) -> AllocResult {
50+
*target = self.try_to_owned()?;
51+
Ok(())
52+
}
53+
}
54+
55+
impl<T: Clone> TryToOwned for [T] {
56+
type Owned = Vec<T>;
57+
58+
fn try_to_owned(&self) -> AllocResult<Vec<T>> {
59+
let mut vec = Vec::new();
60+
self.try_clone_into(&mut vec)?;
61+
Ok(vec)
62+
}
63+
64+
fn try_clone_into(&self, target: &mut Vec<T>) -> AllocResult {
65+
// Ensure target has enough capacity
66+
target
67+
.try_reserve_exact(self.len().saturating_sub(target.len()))
68+
.map_err(|_| AllocError)?;
69+
70+
target.clear();
71+
assume_fallible(|| target.extend_from_slice(self));
72+
Ok(())
73+
}
74+
}
75+
76+
impl TryToOwned for str {
77+
type Owned = String;
78+
79+
fn try_to_owned(&self) -> AllocResult<String> {
80+
let mut vec = String::new();
81+
self.try_clone_into(&mut vec)?;
82+
Ok(vec)
83+
}
84+
85+
fn try_clone_into(&self, target: &mut String) -> AllocResult {
86+
// Ensure target has enough capacity
87+
target
88+
.try_reserve_exact(self.len().saturating_sub(target.len()))
89+
.map_err(|_| AllocError)?;
90+
91+
target.clear();
92+
assume_fallible(|| target.push_str(self));
93+
Ok(())
94+
}
95+
}
96+
97+
/// Trait which provides a fallible methods for [`Vec`].
98+
pub trait VecExt<T> {
99+
/// Faillible alternative to [`Vec::with_capacity`].
100+
fn try_with_capacity(capacity: usize) -> CollectionResult<Self>
101+
where
102+
Self: Sized;
103+
104+
/// Faillible alternative to [`Vec::extend_from_slice`].
105+
fn try_extend_from_slice(&mut self, other: &[T]) -> CollectionResult
106+
where
107+
T: Clone;
108+
109+
/// Faillible alternative to [`Vec::resize`].
110+
fn try_resize(&mut self, new_len: usize, value: T) -> CollectionResult
111+
where
112+
T: Clone;
113+
}
114+
115+
impl<T> VecExt<T> for alloc::vec::Vec<T> {
116+
fn try_with_capacity(capacity: usize) -> CollectionResult<Self> {
117+
let mut vec = Self::new();
118+
vec.try_reserve_exact(capacity)?;
119+
Ok(vec)
120+
}
121+
122+
fn try_extend_from_slice(&mut self, other: &[T]) -> CollectionResult
123+
where
124+
T: Clone,
125+
{
126+
self.try_reserve(other.len())?;
127+
assume_fallible(|| self.extend_from_slice(other));
128+
Ok(())
129+
}
130+
131+
fn try_resize(&mut self, new_len: usize, value: T) -> CollectionResult
132+
where
133+
T: Clone,
134+
{
135+
self.try_reserve(new_len.saturating_sub(self.len()))?;
136+
assume_fallible(|| self.resize(new_len, value));
137+
Ok(())
138+
}
139+
}

samples/rust/rust_minimal.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ impl KernelModule for RustMinimal {
2525
pr_info!("Am I built-in? {}\n", !cfg!(MODULE));
2626

2727
Ok(RustMinimal {
28-
message: "on the heap!".to_owned(),
28+
message: "on the heap!".try_to_owned()?,
2929
})
3030
}
3131
}

0 commit comments

Comments
 (0)