Skip to content

Commit 1c4427c

Browse files
committed
Add read_entry_boxed_in and get_boxed_info_in that use the allocator_api.
This commit adds new functionality behind the `unsafe` feature that enhances some existing functions with `allocator_api`-flavored allocators. Existing code will work with and without the unstable feature.
1 parent 43a951f commit 1c4427c

File tree

7 files changed

+131
-16
lines changed

7 files changed

+131
-16
lines changed

CHANGELOG.md

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
11
# Changelog
22

33
## uefi - [Unreleased]
4+
5+
### Added
46
- Implementations for the trait `EqStrUntilNul` now allow `?Sized` inputs. This means that
57
you can write `some_cstr16.eq_str_until_nul("test")` instead of
68
`some_cstr16.eq_str_until_nul(&"test")` now.
79
- Added `TryFrom<core::ffi::CStr>` implementation for `CStr8`.
810
- Added `Directory::read_entry_boxed` which works similar to `File::get_boxed_info`. This allows
9-
easier iteration over the entries in a directory.
11+
easier iteration over the entries in a directory. (requires the **alloc** feature)
12+
- Added `Directory::read_entry_boxed_in` and `File::get_boxed_info_in` that use the `allocator_api`
13+
feature. (requires the **unstable** and **alloc** features)
1014
- Added an `core::error::Error` implementation for `Error` to ease
11-
integration with error-handling crates.
15+
integration with error-handling crates. (requires the **unstable** feature)
16+
17+
### Changed
18+
19+
### Removed
1220

1321
## uefi-macros - [Unreleased]
1422

uefi-test-runner/Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ publish = false
66
edition = "2021"
77

88
[dependencies]
9-
uefi = { path = "../uefi", features = ['alloc'] }
9+
# TODO we should let the uefi-test-runner run with and without unstable.
10+
uefi = { path = "../uefi", features = ["alloc", "unstable"] }
1011
uefi-services = { path = "../uefi-services" }
1112

1213
log = { version = "0.4.17", default-features = false }

uefi/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ logger = []
1919
# were observed on the VirtualBox UEFI implementation (see uefi-rs#121).
2020
# In those cases, this feature can be excluded by removing the default features.
2121
panic-on-logger-errors = []
22+
# Generic gate to code that uses unstable features of Rust. You usually need a nightly toolchain.
2223
unstable = []
2324

2425
[dependencies]

uefi/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
//! feature is not enabled, but once a couple more required features
3939
//! are stabilized we intend to make the `uefi` crate work on the
4040
//! stable channel by default.
41+
//! As example, in conjunction with the `alloc`-feature, this gate allows
42+
//! the `allocator_api` on certain functions.
4143
//!
4244
//! The `global_allocator` and `logger` features require special
4345
//! handling to perform initialization and tear-down. The
@@ -62,6 +64,7 @@
6264
#![feature(ptr_metadata)]
6365
#![cfg_attr(feature = "alloc", feature(vec_into_raw_parts))]
6466
#![cfg_attr(feature = "unstable", feature(error_in_core))]
67+
#![cfg_attr(all(feature = "unstable", feature = "alloc"), feature(allocator_api))]
6568
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
6669
#![no_std]
6770
// Enable some additional warnings and lints.
@@ -101,5 +104,6 @@ pub mod global_allocator;
101104
#[cfg(feature = "logger")]
102105
pub mod logger;
103106

107+
// As long as this is behind "alloc", we can simplify cfg-feature attributes in this module.
104108
#[cfg(feature = "alloc")]
105109
pub(crate) mod mem;

uefi/src/mem.rs

+59-9
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,19 @@
22
33
use crate::ResultExt;
44
use crate::{Result, Status};
5-
use ::alloc::{alloc, boxed::Box};
5+
use ::alloc::boxed::Box;
66
use core::alloc::Layout;
77
use core::fmt::Debug;
88
use core::slice;
99
use uefi::data_types::Align;
1010
use uefi::Error;
1111

12+
#[cfg(not(feature = "unstable"))]
13+
use ::alloc::alloc::{alloc, dealloc};
14+
15+
#[cfg(feature = "unstable")]
16+
use {core::alloc::Allocator, core::ptr::NonNull};
17+
1218
/// Helper to return owned versions of certain UEFI data structures on the heap in a [`Box`]. This
1319
/// function is intended to wrap low-level UEFI functions of this crate that
1420
/// - can consume an empty buffer without a panic to get the required buffer size in the errors
@@ -17,12 +23,24 @@ use uefi::Error;
1723
/// buffer size is sufficient, and
1824
/// - return a mutable typed reference that points to the same memory as the input buffer on
1925
/// success.
20-
pub fn make_boxed<
26+
///
27+
/// # Feature `unstable` / `allocator_api`
28+
/// By default, this function works with Rust's default allocation mechanism. If you activate the
29+
/// `unstable`-feature, it uses the `allocator_api` instead. In that case, the function takes an
30+
/// additional parameter describing the specific [`Allocator`]. You can use [`alloc::alloc::Global`]
31+
/// as default.
32+
pub(crate) fn make_boxed<
2133
'a,
34+
// The UEFI data structure.
2235
Data: Align + ?Sized + Debug + 'a,
2336
F: FnMut(&'a mut [u8]) -> Result<&'a mut Data, Option<usize>>,
37+
#[cfg(feature = "unstable")] A: Allocator,
2438
>(
39+
// A function to read the UEFI data structure into a provided buffer.
2540
mut fetch_data_fn: F,
41+
#[cfg(feature = "unstable")]
42+
// Allocator of the `allocator_api` feature. You can use `Global` as default.
43+
allocator: A,
2644
) -> Result<Box<Data>> {
2745
let required_size = match fetch_data_fn(&mut []).map_err(Error::split) {
2846
// This is the expected case: the empty buffer passed in is too
@@ -40,13 +58,23 @@ pub fn make_boxed<
4058
.unwrap()
4159
.pad_to_align();
4260

43-
// Allocate the buffer.
44-
let heap_buf: *mut u8 = unsafe {
45-
let ptr = alloc::alloc(layout);
46-
if ptr.is_null() {
47-
return Err(Status::OUT_OF_RESOURCES.into());
61+
// Allocate the buffer on the heap.
62+
let heap_buf: *mut u8 = {
63+
#[cfg(not(feature = "unstable"))]
64+
{
65+
let ptr = unsafe { alloc(layout) };
66+
if ptr.is_null() {
67+
return Err(Status::OUT_OF_RESOURCES.into());
68+
}
69+
ptr
4870
}
49-
ptr
71+
72+
#[cfg(feature = "unstable")]
73+
allocator
74+
.allocate(layout)
75+
.map_err(|_| <Status as Into<Error>>::into(Status::OUT_OF_RESOURCES))?
76+
.as_ptr()
77+
.cast::<u8>()
5078
};
5179

5280
// Read the data into the provided buffer.
@@ -59,7 +87,14 @@ pub fn make_boxed<
5987
let data: &mut Data = match data {
6088
Ok(data) => data,
6189
Err(err) => {
62-
unsafe { alloc::dealloc(heap_buf, layout) };
90+
#[cfg(not(feature = "unstable"))]
91+
unsafe {
92+
dealloc(heap_buf, layout)
93+
};
94+
#[cfg(feature = "unstable")]
95+
unsafe {
96+
allocator.deallocate(NonNull::new(heap_buf).unwrap(), layout)
97+
}
6398
return Err(err);
6499
}
65100
};
@@ -73,6 +108,8 @@ pub fn make_boxed<
73108
mod tests {
74109
use super::*;
75110
use crate::ResultExt;
111+
#[cfg(feature = "unstable")]
112+
use alloc::alloc::Global;
76113
use core::mem::{align_of, size_of};
77114

78115
/// Some simple dummy type to test [`make_boxed`].
@@ -166,14 +203,27 @@ mod tests {
166203
assert_eq!(&data.0 .0, &[1, 2, 3, 4]);
167204
}
168205

206+
/// This unit tests checks the [`make_boxed`] utility. The test has different code and behavior
207+
/// depending on whether the "unstable" feature is active or not.
169208
#[test]
170209
fn test_make_boxed_utility() {
171210
let fetch_data_fn = |buf| uefi_function_stub_read(buf);
211+
212+
#[cfg(not(feature = "unstable"))]
172213
let data: Box<SomeData> = make_boxed(fetch_data_fn).unwrap();
214+
215+
#[cfg(feature = "unstable")]
216+
let data: Box<SomeData> = make_boxed(fetch_data_fn, Global).unwrap();
173217
assert_eq!(&data.0, &[1, 2, 3, 4]);
174218

175219
let fetch_data_fn = |buf| uefi_function_stub_read(buf);
220+
221+
#[cfg(not(feature = "unstable"))]
176222
let data: Box<SomeDataAlign16> = make_boxed(fetch_data_fn).unwrap();
223+
224+
#[cfg(feature = "unstable")]
225+
let data: Box<SomeDataAlign16> = make_boxed(fetch_data_fn, Global).unwrap();
226+
177227
assert_eq!(&data.0 .0, &[1, 2, 3, 4]);
178228
}
179229
}

uefi/src/proto/media/file/dir.rs

+38-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ use super::{File, FileHandle, FileInfo, FromUefi, RegularFile};
22
use crate::data_types::Align;
33
use crate::Result;
44
use core::ffi::c_void;
5-
65
#[cfg(feature = "alloc")]
76
use {crate::mem::make_boxed, alloc::boxed::Box};
7+
#[cfg(all(feature = "unstable", feature = "alloc"))]
8+
use {alloc::alloc::Global, core::alloc::Allocator};
89

910
/// A `FileHandle` that is also a directory.
1011
///
@@ -62,8 +63,7 @@ impl Directory {
6263
})
6364
}
6465

65-
/// Wrapper around [`Self::read_entry`] that returns an owned copy of the data. It has the same
66-
/// implications and requirements. On failure, the payload of `Err` is `()´.
66+
/// Wrapper around [`Self::read_entry_boxed_in`] that uses the [`Global`] allocator.
6767
#[cfg(feature = "alloc")]
6868
pub fn read_entry_boxed(&mut self) -> Result<Option<Box<FileInfo>>> {
6969
let read_entry_res = self.read_entry(&mut []);
@@ -80,7 +80,42 @@ impl Directory {
8080
maybe_info.expect("Should have more entries")
8181
})
8282
};
83+
84+
#[cfg(not(feature = "unstable"))]
8385
let file_info = make_boxed::<FileInfo, _>(fetch_data_fn)?;
86+
87+
#[cfg(feature = "unstable")]
88+
let file_info = make_boxed::<FileInfo, _, _>(fetch_data_fn, Global)?;
89+
90+
Ok(Some(file_info))
91+
}
92+
93+
/// Wrapper around [`Self::read_entry`] that returns an owned copy of the data. It has the same
94+
/// implications and requirements. On failure, the payload of `Err` is `()´.
95+
///
96+
/// It allows to use a custom allocator via the `allocator_api` feature.
97+
#[cfg(all(feature = "unstable", feature = "alloc"))]
98+
pub fn read_entry_boxed_in<A: Allocator>(
99+
&mut self,
100+
allocator: A,
101+
) -> Result<Option<Box<FileInfo>>> {
102+
let read_entry_res = self.read_entry(&mut []);
103+
104+
// If no more entries are available, return early.
105+
if let Ok(None) = read_entry_res {
106+
return Ok(None);
107+
}
108+
109+
let fetch_data_fn = |buf| {
110+
self.read_entry(buf)
111+
// this is safe, as above, we checked that there are more entries
112+
.map(|maybe_info: Option<&mut FileInfo>| {
113+
maybe_info.expect("Should have more entries")
114+
})
115+
};
116+
117+
let file_info = make_boxed::<FileInfo, _, A>(fetch_data_fn, allocator)?;
118+
84119
Ok(Some(file_info))
85120
}
86121

uefi/src/proto/media/file/mod.rs

+17-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ use core::ffi::c_void;
1616
use core::fmt::Debug;
1717
use core::mem;
1818
use core::ptr;
19+
#[cfg(all(feature = "unstable", feature = "alloc"))]
20+
use {alloc::alloc::Global, core::alloc::Allocator};
1921
#[cfg(feature = "alloc")]
2022
use {alloc::boxed::Box, uefi::mem::make_boxed};
2123

@@ -161,11 +163,25 @@ pub trait File: Sized {
161163
(self.imp().flush)(self.imp()).into()
162164
}
163165

166+
/// Wrapper around [`Self::get_boxed_info_in`] that uses the [`Global`] allocator.
164167
#[cfg(feature = "alloc")]
165-
/// Read the dynamically allocated info for a file.
166168
fn get_boxed_info<Info: FileProtocolInfo + ?Sized + Debug>(&mut self) -> Result<Box<Info>> {
167169
let fetch_data_fn = |buf| self.get_info::<Info>(buf);
170+
#[cfg(not(feature = "unstable"))]
168171
let file_info = make_boxed::<Info, _>(fetch_data_fn)?;
172+
#[cfg(feature = "unstable")]
173+
let file_info = make_boxed::<Info, _, _>(fetch_data_fn, Global)?;
174+
Ok(file_info)
175+
}
176+
177+
/// Read the dynamically allocated info for a file.
178+
#[cfg(all(feature = "unstable", feature = "alloc"))]
179+
fn get_boxed_info_in<Info: FileProtocolInfo + ?Sized + Debug, A: Allocator>(
180+
&mut self,
181+
allocator: A,
182+
) -> Result<Box<Info>> {
183+
let fetch_data_fn = |buf| self.get_info::<Info>(buf);
184+
let file_info = make_boxed::<Info, _, A>(fetch_data_fn, allocator)?;
169185
Ok(file_info)
170186
}
171187

0 commit comments

Comments
 (0)