Skip to content

Commit b936515

Browse files
Add CStr16::from_char16_with_nul[_unchecked]
These new methods mirror `from_u16_with_nul` and `from_u16_with_nul_unchecked`, but take a `&[Char16]` slice instead of `&[u16]`.
1 parent 05a8211 commit b936515

File tree

2 files changed

+74
-0
lines changed

2 files changed

+74
-0
lines changed

uefi/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## Added
44
- Implemented `PartialEq<char>` for `Char8` and `Char16`.
5+
- Added `CStr16::from_char16_with_nul` and `Char16::from_char16_with_nul_unchecked`.
56

67
# uefi - 0.26.0 (2023-11-12)
78

uefi/src/data_types/strs.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,40 @@ impl CStr16 {
284284
&*(codes as *const [u16] as *const Self)
285285
}
286286

287+
/// Creates a `&CStr16` from a [`Char16`] slice, if the slice is
288+
/// null-terminated and has no interior null characters.
289+
pub fn from_char16_with_nul(chars: &[Char16]) -> Result<&Self, FromSliceWithNulError> {
290+
// Fail early if the input is empty.
291+
if chars.is_empty() {
292+
return Err(FromSliceWithNulError::NotNulTerminated);
293+
}
294+
295+
// Find the index of the first null char.
296+
if let Some(null_index) = chars.iter().position(|c| *c == NUL_16) {
297+
// Verify the null character is at the end.
298+
if null_index == chars.len() - 1 {
299+
// Safety: the input is null-terminated and has no interior nulls.
300+
Ok(unsafe { Self::from_char16_with_nul_unchecked(chars) })
301+
} else {
302+
Err(FromSliceWithNulError::InteriorNul(null_index))
303+
}
304+
} else {
305+
Err(FromSliceWithNulError::NotNulTerminated)
306+
}
307+
}
308+
309+
/// Unsafely creates a `&CStr16` from a `Char16` slice.
310+
///
311+
/// # Safety
312+
///
313+
/// It's the callers responsibility to ensure chars is null-terminated and
314+
/// has no interior null characters.
315+
#[must_use]
316+
pub const unsafe fn from_char16_with_nul_unchecked(chars: &[Char16]) -> &Self {
317+
let ptr: *const [Char16] = chars;
318+
&*(ptr as *const Self)
319+
}
320+
287321
/// Convert a [`&str`] to a `&CStr16`, backed by a buffer.
288322
///
289323
/// The input string must contain only characters representable with
@@ -615,6 +649,45 @@ mod tests {
615649
assert_eq!(s.num_bytes(), 8);
616650
}
617651

652+
#[test]
653+
fn test_cstr16_from_char16_with_nul() {
654+
// Invalid: empty input.
655+
assert_eq!(
656+
CStr16::from_char16_with_nul(&[]),
657+
Err(FromSliceWithNulError::NotNulTerminated)
658+
);
659+
660+
// Invalid: interior null.
661+
assert_eq!(
662+
CStr16::from_char16_with_nul(&[
663+
Char16::try_from('a').unwrap(),
664+
NUL_16,
665+
Char16::try_from('b').unwrap(),
666+
NUL_16
667+
]),
668+
Err(FromSliceWithNulError::InteriorNul(1))
669+
);
670+
671+
// Invalid: no trailing null.
672+
assert_eq!(
673+
CStr16::from_char16_with_nul(&[
674+
Char16::try_from('a').unwrap(),
675+
Char16::try_from('b').unwrap(),
676+
]),
677+
Err(FromSliceWithNulError::NotNulTerminated)
678+
);
679+
680+
// Valid.
681+
assert_eq!(
682+
CStr16::from_char16_with_nul(&[
683+
Char16::try_from('a').unwrap(),
684+
Char16::try_from('b').unwrap(),
685+
NUL_16,
686+
]),
687+
Ok(cstr16!("ab"))
688+
);
689+
}
690+
618691
#[test]
619692
fn test_cstr16_from_str_with_buf() {
620693
let mut buf = [0; 4];

0 commit comments

Comments
 (0)