Skip to content

Commit

Permalink
Add CStr16::from_char16_with_nul[_unchecked]
Browse files Browse the repository at this point in the history
These new methods mirror `from_u16_with_nul` and `from_u16_with_nul_unchecked`,
but take a `&[Char16]` slice instead of `&[u16]`.
  • Loading branch information
nicholasbishop committed Nov 30, 2023
1 parent 05a8211 commit b936515
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 0 deletions.
1 change: 1 addition & 0 deletions uefi/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Added
- Implemented `PartialEq<char>` for `Char8` and `Char16`.
- Added `CStr16::from_char16_with_nul` and `Char16::from_char16_with_nul_unchecked`.

# uefi - 0.26.0 (2023-11-12)

Expand Down
73 changes: 73 additions & 0 deletions uefi/src/data_types/strs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,40 @@ impl CStr16 {
&*(codes as *const [u16] as *const Self)
}

/// Creates a `&CStr16` from a [`Char16`] slice, if the slice is
/// null-terminated and has no interior null characters.
pub fn from_char16_with_nul(chars: &[Char16]) -> Result<&Self, FromSliceWithNulError> {
// Fail early if the input is empty.
if chars.is_empty() {
return Err(FromSliceWithNulError::NotNulTerminated);
}

// Find the index of the first null char.
if let Some(null_index) = chars.iter().position(|c| *c == NUL_16) {
// Verify the null character is at the end.
if null_index == chars.len() - 1 {
// Safety: the input is null-terminated and has no interior nulls.
Ok(unsafe { Self::from_char16_with_nul_unchecked(chars) })
} else {
Err(FromSliceWithNulError::InteriorNul(null_index))
}
} else {
Err(FromSliceWithNulError::NotNulTerminated)
}
}

/// Unsafely creates a `&CStr16` from a `Char16` slice.
///
/// # Safety
///
/// It's the callers responsibility to ensure chars is null-terminated and
/// has no interior null characters.
#[must_use]
pub const unsafe fn from_char16_with_nul_unchecked(chars: &[Char16]) -> &Self {
let ptr: *const [Char16] = chars;
&*(ptr as *const Self)
}

/// Convert a [`&str`] to a `&CStr16`, backed by a buffer.
///
/// The input string must contain only characters representable with
Expand Down Expand Up @@ -615,6 +649,45 @@ mod tests {
assert_eq!(s.num_bytes(), 8);
}

#[test]
fn test_cstr16_from_char16_with_nul() {
// Invalid: empty input.
assert_eq!(
CStr16::from_char16_with_nul(&[]),
Err(FromSliceWithNulError::NotNulTerminated)
);

// Invalid: interior null.
assert_eq!(
CStr16::from_char16_with_nul(&[
Char16::try_from('a').unwrap(),
NUL_16,
Char16::try_from('b').unwrap(),
NUL_16
]),
Err(FromSliceWithNulError::InteriorNul(1))
);

// Invalid: no trailing null.
assert_eq!(
CStr16::from_char16_with_nul(&[
Char16::try_from('a').unwrap(),
Char16::try_from('b').unwrap(),
]),
Err(FromSliceWithNulError::NotNulTerminated)
);

// Valid.
assert_eq!(
CStr16::from_char16_with_nul(&[
Char16::try_from('a').unwrap(),
Char16::try_from('b').unwrap(),
NUL_16,
]),
Ok(cstr16!("ab"))
);
}

#[test]
fn test_cstr16_from_str_with_buf() {
let mut buf = [0; 4];
Expand Down

0 comments on commit b936515

Please sign in to comment.