Skip to content

Commit 58ae6a4

Browse files
nicholasbishopGabrielMajeri
authored andcommitted
Add CString16
For now the API is quite minimal; mostly it just makes interop between regular Rust strings and EFI strings easier. You can convert from a `&str` to a `CString16`, and since it derefs to `&CStr16` you can call the methods exposed by that type as well. This type is gated by the `exts` feature since it requires allocation.
1 parent ba9504f commit 58ae6a4

File tree

4 files changed

+103
-23
lines changed

4 files changed

+103
-23
lines changed

src/data_types/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,8 @@ mod enums;
5151

5252
mod strs;
5353
pub use self::strs::{CStr16, CStr8, FromSliceWithNulError};
54+
55+
#[cfg(feature = "exts")]
56+
mod owned_strs;
57+
#[cfg(feature = "exts")]
58+
pub use self::owned_strs::{CString16, FromStrError};

src/data_types/owned_strs.rs

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
use super::chars::{Char16, NUL_16};
2+
use super::strs::CStr16;
3+
use crate::alloc_api::vec::Vec;
4+
use core::convert::TryFrom;
5+
use core::ops;
6+
7+
/// Error returned by [`CString16::try_from::<&str>`].
8+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
9+
pub enum FromStrError {
10+
/// Character conversion error.
11+
InvalidChar,
12+
/// Nul character found in the input.
13+
InteriorNul,
14+
}
15+
16+
/// An owned UCS-2 null-terminated string.
17+
///
18+
/// # Examples
19+
///
20+
/// Round-trip conversion from a [`&str`] to a `CString16` and back:
21+
///
22+
/// ```
23+
/// use core::convert::TryFrom;
24+
/// use uefi::CString16;
25+
///
26+
/// let s = CString16::try_from("abc").unwrap();
27+
/// assert_eq!(s.as_string(), "abc");
28+
/// ```
29+
#[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd)]
30+
pub struct CString16(Vec<Char16>);
31+
32+
impl TryFrom<&str> for CString16 {
33+
type Error = FromStrError;
34+
35+
fn try_from(input: &str) -> Result<Self, Self::Error> {
36+
// Initially allocate one Char16 for each byte of the input, plus
37+
// one for the null byte. This should be a good guess for ASCII-ish
38+
// input.
39+
let mut output = Vec::with_capacity(input.len() + 1);
40+
41+
// Convert to UTF-16, then convert to UCS-2.
42+
for c in input.encode_utf16() {
43+
let c = Char16::try_from(c).map_err(|_| FromStrError::InvalidChar)?;
44+
45+
// Check for interior nul chars.
46+
if c == NUL_16 {
47+
return Err(FromStrError::InteriorNul);
48+
}
49+
50+
output.push(c);
51+
}
52+
53+
// Add trailing nul.
54+
output.push(NUL_16);
55+
56+
Ok(CString16(output))
57+
}
58+
}
59+
60+
impl ops::Deref for CString16 {
61+
type Target = CStr16;
62+
63+
fn deref(&self) -> &CStr16 {
64+
unsafe { &*(self.0.as_slice() as *const [Char16] as *const CStr16) }
65+
}
66+
}
67+
68+
impl AsRef<CStr16> for CString16 {
69+
fn as_ref(&self) -> &CStr16 {
70+
self
71+
}
72+
}
73+
74+
#[cfg(test)]
75+
mod tests {
76+
use super::*;
77+
use crate::alloc_api::vec;
78+
79+
#[test]
80+
fn test_cstring16_from_str() {
81+
assert_eq!(
82+
CString16::try_from("x").unwrap(),
83+
CString16(vec![Char16::try_from('x').unwrap(), NUL_16])
84+
);
85+
86+
assert_eq!(CString16::try_from("😀"), Err(FromStrError::InvalidChar));
87+
88+
assert_eq!(CString16::try_from("x\0"), Err(FromStrError::InteriorNul));
89+
}
90+
}

src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ extern crate self as uefi;
4545

4646
#[macro_use]
4747
pub mod data_types;
48+
#[cfg(feature = "exts")]
49+
pub use self::data_types::CString16;
4850
pub use self::data_types::{unsafe_guid, Identify};
4951
pub use self::data_types::{CStr16, CStr8, Char16, Char8, Event, Guid, Handle};
5052

uefi-test-runner/src/runtime/vars.rs

+6-23
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,11 @@
1-
use alloc::vec::Vec;
1+
use core::convert::TryFrom;
22
use log::info;
33
use uefi::prelude::*;
44
use uefi::table::runtime::{VariableAttributes, VariableVendor};
5-
use uefi::{CStr16, Guid};
6-
7-
struct CString16(Vec<u16>);
8-
9-
impl CString16 {
10-
fn from_str(input: &str) -> CString16 {
11-
let mut v: Vec<u16> = input.encode_utf16().collect();
12-
v.push(0);
13-
CString16(v)
14-
}
15-
16-
fn as_cstr16(&self) -> &CStr16 {
17-
match CStr16::from_u16_with_nul(&self.0) {
18-
Ok(s) => s,
19-
Err(_) => panic!("invalid string"),
20-
}
21-
}
22-
}
5+
use uefi::{CString16, Guid};
236

247
fn test_variables(rt: &RuntimeServices) {
25-
let name = CString16::from_str("UefiRsTestVar");
8+
let name = CString16::try_from("UefiRsTestVar").unwrap();
269
let test_value = b"TestValue";
2710
let test_attrs = VariableAttributes::BOOTSERVICE_ACCESS | VariableAttributes::RUNTIME_ACCESS;
2811

@@ -36,19 +19,19 @@ fn test_variables(rt: &RuntimeServices) {
3619
));
3720

3821
info!("Testing set_variable");
39-
rt.set_variable(name.as_cstr16(), &vendor, test_attrs, test_value)
22+
rt.set_variable(&name, &vendor, test_attrs, test_value)
4023
.expect_success("failed to set variable");
4124

4225
info!("Testing get_variable_size");
4326
let size = rt
44-
.get_variable_size(name.as_cstr16(), &vendor)
27+
.get_variable_size(&name, &vendor)
4528
.expect_success("failed to get variable size");
4629
assert_eq!(size, test_value.len());
4730

4831
info!("Testing get_variable");
4932
let mut buf = [0u8; 9];
5033
let (data, attrs) = rt
51-
.get_variable(name.as_cstr16(), &vendor, &mut buf)
34+
.get_variable(&name, &vendor, &mut buf)
5235
.expect_success("failed to get variable");
5336
assert_eq!(data, test_value);
5437
assert_eq!(attrs, test_attrs);

0 commit comments

Comments
 (0)