Skip to content

Commit 5be61ad

Browse files
Add CStr16::from_str_with_buf (#291)
1 parent c00bb56 commit 5be61ad

File tree

3 files changed

+95
-4
lines changed

3 files changed

+95
-4
lines changed

src/data_types/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ pub use self::chars::{Char16, Char8};
5757
mod enums;
5858

5959
mod strs;
60-
pub use self::strs::{CStr16, CStr8, FromSliceWithNulError};
60+
pub use self::strs::{CStr16, CStr8, FromSliceWithNulError, FromStrWithBufError};
6161

6262
#[cfg(feature = "exts")]
6363
mod owned_strs;

src/data_types/strs.rs

+91
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,20 @@ pub enum FromSliceWithNulError {
2020
NotNulTerminated,
2121
}
2222

23+
/// Error returned by [`CStr16::from_str_with_buf`].
24+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
25+
pub enum FromStrWithBufError {
26+
/// An invalid character was encountered before the end of the string
27+
InvalidChar(usize),
28+
29+
/// A null character was encountered in the string
30+
InteriorNul(usize),
31+
32+
/// The buffer is not big enough to hold the entire string and
33+
/// trailing null character
34+
BufferTooSmall,
35+
}
36+
2337
/// A Latin-1 null-terminated string
2438
///
2539
/// This type is largely inspired by `std::ffi::CStr`, see the documentation of
@@ -141,6 +155,52 @@ impl CStr16 {
141155
&*(codes as *const [u16] as *const Self)
142156
}
143157

158+
/// Convert a [`&str`] to a `&CStr16`, backed by a buffer.
159+
///
160+
/// The input string must contain only characters representable with
161+
/// UCS-2, and must not contain any null characters (even at the end of
162+
/// the input).
163+
///
164+
/// The backing buffer must be big enough to hold the converted string as
165+
/// well as a trailing null character.
166+
///
167+
/// # Examples
168+
///
169+
/// Convert the UTF-8 string "ABC" to a `&CStr16`:
170+
///
171+
/// ```
172+
/// use uefi::CStr16;
173+
///
174+
/// let mut buf = [0; 4];
175+
/// CStr16::from_str_with_buf("ABC", &mut buf).unwrap();
176+
/// ```
177+
pub fn from_str_with_buf<'a>(
178+
input: &str,
179+
buf: &'a mut [u16],
180+
) -> Result<&'a Self, FromStrWithBufError> {
181+
let mut index = 0;
182+
183+
// Convert to UTF-16.
184+
for c in input.encode_utf16() {
185+
*buf.get_mut(index)
186+
.ok_or(FromStrWithBufError::BufferTooSmall)? = c;
187+
index += 1;
188+
}
189+
190+
// Add trailing null character.
191+
*buf.get_mut(index)
192+
.ok_or(FromStrWithBufError::BufferTooSmall)? = 0;
193+
194+
// Convert from u16 to Char16. This checks for invalid UCS-2 chars and
195+
// interior nulls. The NotNulTerminated case is unreachable because we
196+
// just added a trailing null character.
197+
Self::from_u16_with_nul(&buf[..index + 1]).map_err(|err| match err {
198+
FromSliceWithNulError::InvalidChar(p) => FromStrWithBufError::InvalidChar(p),
199+
FromSliceWithNulError::InteriorNul(p) => FromStrWithBufError::InteriorNul(p),
200+
FromSliceWithNulError::NotNulTerminated => unreachable!(),
201+
})
202+
}
203+
144204
/// Returns the inner pointer to this C string
145205
pub fn as_ptr(&self) -> *const Char16 {
146206
self.0.as_ptr()
@@ -251,4 +311,35 @@ mod tests {
251311
let s = CStr16::from_u16_with_nul(&[65, 66, 67, 0]).unwrap();
252312
assert_eq!(s.num_bytes(), 8);
253313
}
314+
315+
#[test]
316+
fn test_cstr16_from_str_with_buf() {
317+
let mut buf = [0; 4];
318+
319+
// OK: buf is exactly the right size.
320+
let s = CStr16::from_str_with_buf("ABC", &mut buf).unwrap();
321+
assert_eq!(s.to_u16_slice_with_nul(), [65, 66, 67, 0]);
322+
323+
// OK: buf is bigger than needed.
324+
let s = CStr16::from_str_with_buf("A", &mut buf).unwrap();
325+
assert_eq!(s.to_u16_slice_with_nul(), [65, 0]);
326+
327+
// Error: buf is too small.
328+
assert_eq!(
329+
CStr16::from_str_with_buf("ABCD", &mut buf).unwrap_err(),
330+
FromStrWithBufError::BufferTooSmall
331+
);
332+
333+
// Error: invalid character.
334+
assert_eq!(
335+
CStr16::from_str_with_buf("a😀", &mut buf).unwrap_err(),
336+
FromStrWithBufError::InvalidChar(1),
337+
);
338+
339+
// Error: interior null.
340+
assert_eq!(
341+
CStr16::from_str_with_buf("a\0b", &mut buf).unwrap_err(),
342+
FromStrWithBufError::InteriorNul(1),
343+
);
344+
}
254345
}

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
use core::convert::TryFrom;
21
use log::info;
32
use uefi::prelude::*;
43
use uefi::table::runtime::{VariableAttributes, VariableVendor};
5-
use uefi::{CString16, Guid};
4+
use uefi::{CStr16, Guid};
65

76
fn test_variables(rt: &RuntimeServices) {
8-
let name = CString16::try_from("UefiRsTestVar").unwrap();
7+
let mut buf = [0; 14];
8+
let name = CStr16::from_str_with_buf("UefiRsTestVar", &mut buf).unwrap();
99
let test_value = b"TestValue";
1010
let test_attrs = VariableAttributes::BOOTSERVICE_ACCESS | VariableAttributes::RUNTIME_ACCESS;
1111

0 commit comments

Comments
 (0)