Skip to content

Add fallible allocation methods #325

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

Closed
wants to merge 1 commit 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
6 changes: 3 additions & 3 deletions rust/kernel/io_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

//! Buffers used in IO.

use crate::traits::VecExt;
use crate::Result;
use alloc::vec::Vec;
use core::mem::{size_of, MaybeUninit};
Expand Down Expand Up @@ -29,9 +30,8 @@ pub trait IoBufferReader {
///
/// Returns `EFAULT` if the address does not currently point to mapped, readable memory.
fn read_all(&mut self) -> Result<Vec<u8>> {
let mut data = Vec::<u8>::new();
data.try_reserve_exact(self.len())?;
data.resize(self.len(), 0);
let mut data = Vec::try_with_capacity(self.len())?;
data.try_resize(self.len(), 0)?;

// SAFETY: The output buffer is valid as we just allocated it.
unsafe { self.read_raw(data.as_mut_ptr(), data.len())? };
Expand Down
5 changes: 2 additions & 3 deletions rust/kernel/module_param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
//! C header: [`include/linux/moduleparam.h`](../../../include/linux/moduleparam.h)

use crate::str::CStr;
use crate::traits::TryToOwned;
use core::fmt::Write;

/// Types that can be used for module parameters.
Expand Down Expand Up @@ -475,9 +476,7 @@ impl ModuleParam for StringParam {
let slab_available = unsafe { crate::bindings::slab_is_available() };
arg.and_then(|arg| {
if slab_available {
let mut vec = alloc::vec::Vec::new();
vec.try_reserve_exact(arg.len()).ok()?;
vec.extend_from_slice(arg);
let vec = arg.try_to_owned().ok()?;
Some(StringParam::Owned(vec))
} else {
Some(StringParam::Ref(arg))
Expand Down
2 changes: 1 addition & 1 deletion rust/kernel/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ pub use super::static_assert;

pub use super::{KernelModule, Result};

pub use crate::traits::TryPin;
pub use crate::traits::{TryPin, TryToOwned, VecExt as _};
113 changes: 113 additions & 0 deletions rust/kernel/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,19 @@

use core::{ops::Deref, pin::Pin};

use alloc::collections::TryReserveError;
use alloc::string::String;
use alloc::vec::Vec;
use alloc::{alloc::AllocError, sync::Arc};

type AllocResult<T = ()> = core::result::Result<T, AllocError>;
type CollectionResult<T = ()> = core::result::Result<T, TryReserveError>;

#[inline]
fn assume_fallible<T, F: FnOnce() -> T>(f: F) -> T {
f()
}

/// Trait which provides a fallible version of `pin()` for pointer types.
///
/// Common pointer types which implement a `pin()` method include [`Box`](alloc::boxed::Box) and [`Arc`].
Expand All @@ -24,3 +35,105 @@ impl<T> TryPin<Arc<T>> for Arc<T> {
Ok(unsafe { Pin::new_unchecked(Arc::try_new(data)?) })
}
}

/// Faillible alternative to [`alloc::borrow::ToOwned`].
pub trait TryToOwned {
/// The resulting type after obtaining ownership.
type Owned: core::borrow::Borrow<Self>;

/// Faillible alternative to [`alloc::borrow::ToOwned::to_owned`].
#[must_use = "cloning is often expensive and is not expected to have side effects"]
fn try_to_owned(&self) -> AllocResult<Self::Owned>;

/// Faillible alternative to [`alloc::borrow::ToOwned::clone_into`].
fn try_clone_into(&self, target: &mut Self::Owned) -> AllocResult {
*target = self.try_to_owned()?;
Ok(())
}
}

impl<T: Clone> TryToOwned for [T] {
type Owned = Vec<T>;

fn try_to_owned(&self) -> AllocResult<Vec<T>> {
let mut vec = Vec::new();
self.try_clone_into(&mut vec)?;
Ok(vec)
}

fn try_clone_into(&self, target: &mut Vec<T>) -> AllocResult {
// Ensure target has enough capacity
target
.try_reserve_exact(self.len().saturating_sub(target.len()))
.map_err(|_| AllocError)?;

target.clear();
assume_fallible(|| target.extend_from_slice(self));
Ok(())
}
}

impl TryToOwned for str {
type Owned = String;

fn try_to_owned(&self) -> AllocResult<String> {
let mut vec = String::new();
self.try_clone_into(&mut vec)?;
Ok(vec)
}

fn try_clone_into(&self, target: &mut String) -> AllocResult {
// Ensure target has enough capacity
target
.try_reserve_exact(self.len().saturating_sub(target.len()))
.map_err(|_| AllocError)?;

target.clear();
assume_fallible(|| target.push_str(self));
Ok(())
}
}

/// Trait which provides a fallible methods for [`Vec`].
pub trait VecExt<T> {
/// Faillible alternative to [`Vec::with_capacity`].
fn try_with_capacity(capacity: usize) -> CollectionResult<Self>
where
Self: Sized;

/// Faillible alternative to [`Vec::extend_from_slice`].
fn try_extend_from_slice(&mut self, other: &[T]) -> CollectionResult
where
T: Clone;

/// Faillible alternative to [`Vec::resize`].
fn try_resize(&mut self, new_len: usize, value: T) -> CollectionResult
where
T: Clone;
}

impl<T> VecExt<T> for alloc::vec::Vec<T> {
fn try_with_capacity(capacity: usize) -> CollectionResult<Self> {
let mut vec = Self::new();
vec.try_reserve_exact(capacity)?;
Ok(vec)
}

fn try_extend_from_slice(&mut self, other: &[T]) -> CollectionResult
where
T: Clone,
{
self.try_reserve(other.len())?;
assume_fallible(|| self.extend_from_slice(other));
Ok(())
}

fn try_resize(&mut self, new_len: usize, value: T) -> CollectionResult
where
T: Clone,
{
self.try_reserve(new_len.saturating_sub(self.len()))?;
assume_fallible(|| self.resize(new_len, value));
Ok(())
}
}
2 changes: 1 addition & 1 deletion samples/rust/rust_minimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ impl KernelModule for RustMinimal {
pr_info!("Am I built-in? {}\n", !cfg!(MODULE));

Ok(RustMinimal {
message: "on the heap!".to_owned(),
message: "on the heap!".try_to_owned()?,
})
}
}
Expand Down