Skip to content

Commit 7154676

Browse files
authored
Merge pull request #1422 from nicholasbishop/bishop-until-nul
uefi: Add CStr16 conversions from slices with interior nuls
2 parents 5fce46d + 25b3dc0 commit 7154676

File tree

2 files changed

+128
-1
lines changed

2 files changed

+128
-1
lines changed

uefi/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ details of the deprecated items that were removed in this release.
55

66
## Added
77
- Impl `PartialEq` and `Eq` for `GptPartitionEntry`.
8+
- Added `CStr16::from_u16_until_nul` and `CStr16::from_char16_until_nul`.
89

910
## Changed
1011
- **Breaking:** Deleted the deprecated `BootServices`, `RuntimeServices`, and

uefi/src/data_types/strs.rs

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,31 @@ use core::slice;
1010
#[cfg(feature = "alloc")]
1111
use super::CString16;
1212

13-
/// Errors which can occur during checked `[uN]` -> `CStrN` conversions
13+
/// Error converting from a slice (which can contain interior nuls) to a string
14+
/// type.
15+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
16+
pub enum FromSliceUntilNulError {
17+
/// An invalid character was encountered before the end of the slice.
18+
InvalidChar(usize),
19+
20+
/// The does not contain a nul character.
21+
NoNul,
22+
}
23+
24+
impl Display for FromSliceUntilNulError {
25+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
26+
match self {
27+
Self::InvalidChar(usize) => write!(f, "invalid character at index {}", usize),
28+
Self::NoNul => write!(f, "no nul character"),
29+
}
30+
}
31+
}
32+
33+
#[cfg(feature = "unstable")]
34+
impl core::error::Error for FromSliceUntilNulError {}
35+
36+
/// Error converting from a slice (which cannot contain interior nuls) to a
37+
/// string type.
1438
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1539
pub enum FromSliceWithNulError {
1640
/// An invalid character was encountered before the end of the slice
@@ -337,6 +361,23 @@ impl CStr16 {
337361
Self::from_u16_with_nul_unchecked(slice::from_raw_parts(ptr, len + 1))
338362
}
339363

364+
/// Creates a `&CStr16` from a u16 slice, stopping at the first nul character.
365+
///
366+
/// # Errors
367+
///
368+
/// An error is returned if the slice contains invalid UCS-2 characters, or
369+
/// if the slice does not contain any nul character.
370+
pub fn from_u16_until_nul(codes: &[u16]) -> Result<&Self, FromSliceUntilNulError> {
371+
for (pos, &code) in codes.iter().enumerate() {
372+
let chr =
373+
Char16::try_from(code).map_err(|_| FromSliceUntilNulError::InvalidChar(pos))?;
374+
if chr == NUL_16 {
375+
return Ok(unsafe { Self::from_u16_with_nul_unchecked(&codes[..=pos]) });
376+
}
377+
}
378+
Err(FromSliceUntilNulError::NoNul)
379+
}
380+
340381
/// Creates a `&CStr16` from a u16 slice, if the slice contains exactly
341382
/// one terminating null-byte and all chars are valid UCS-2 chars.
342383
pub fn from_u16_with_nul(codes: &[u16]) -> Result<&Self, FromSliceWithNulError> {
@@ -369,6 +410,22 @@ impl CStr16 {
369410
&*(codes as *const [u16] as *const Self)
370411
}
371412

413+
/// Creates a `&CStr16` from a [`Char16`] slice, stopping at the first nul character.
414+
///
415+
/// # Errors
416+
///
417+
/// An error is returned if the slice does not contain any nul character.
418+
pub fn from_char16_until_nul(chars: &[Char16]) -> Result<&Self, FromSliceUntilNulError> {
419+
// Find the index of the first null char.
420+
let end = chars
421+
.iter()
422+
.position(|c| *c == NUL_16)
423+
.ok_or(FromSliceUntilNulError::NoNul)?;
424+
425+
// Safety: the input is nul-terminated.
426+
unsafe { Ok(Self::from_char16_with_nul_unchecked(&chars[..=end])) }
427+
}
428+
372429
/// Creates a `&CStr16` from a [`Char16`] slice, if the slice is
373430
/// null-terminated and has no interior null characters.
374431
pub fn from_char16_with_nul(chars: &[Char16]) -> Result<&Self, FromSliceWithNulError> {
@@ -734,6 +791,75 @@ mod tests {
734791
assert_eq!(s.num_bytes(), 8);
735792
}
736793

794+
#[test]
795+
fn test_cstr16_from_u16_until_nul() {
796+
// Invalid: empty input.
797+
assert_eq!(
798+
CStr16::from_u16_until_nul(&[]),
799+
Err(FromSliceUntilNulError::NoNul)
800+
);
801+
802+
// Invalid: no nul.
803+
assert_eq!(
804+
CStr16::from_u16_until_nul(&[65, 66]),
805+
Err(FromSliceUntilNulError::NoNul)
806+
);
807+
808+
// Invalid: not UCS-2.
809+
assert_eq!(
810+
CStr16::from_u16_until_nul(&[65, 0xde01, 0]),
811+
Err(FromSliceUntilNulError::InvalidChar(1))
812+
);
813+
814+
// Valid: trailing nul.
815+
assert_eq!(CStr16::from_u16_until_nul(&[97, 98, 0,]), Ok(cstr16!("ab")));
816+
817+
// Valid: interior nul.
818+
assert_eq!(
819+
CStr16::from_u16_until_nul(&[97, 0, 98, 0,]),
820+
Ok(cstr16!("a"))
821+
);
822+
}
823+
824+
#[test]
825+
fn test_cstr16_from_char16_until_nul() {
826+
// Invalid: empty input.
827+
assert_eq!(
828+
CStr16::from_char16_until_nul(&[]),
829+
Err(FromSliceUntilNulError::NoNul)
830+
);
831+
832+
// Invalid: no nul character.
833+
assert_eq!(
834+
CStr16::from_char16_until_nul(&[
835+
Char16::try_from('a').unwrap(),
836+
Char16::try_from('b').unwrap(),
837+
]),
838+
Err(FromSliceUntilNulError::NoNul)
839+
);
840+
841+
// Valid: trailing nul.
842+
assert_eq!(
843+
CStr16::from_char16_until_nul(&[
844+
Char16::try_from('a').unwrap(),
845+
Char16::try_from('b').unwrap(),
846+
NUL_16,
847+
]),
848+
Ok(cstr16!("ab"))
849+
);
850+
851+
// Valid: interior nul.
852+
assert_eq!(
853+
CStr16::from_char16_until_nul(&[
854+
Char16::try_from('a').unwrap(),
855+
NUL_16,
856+
Char16::try_from('b').unwrap(),
857+
NUL_16
858+
]),
859+
Ok(cstr16!("a"))
860+
);
861+
}
862+
737863
#[test]
738864
fn test_cstr16_from_char16_with_nul() {
739865
// Invalid: empty input.

0 commit comments

Comments
 (0)