diff --git a/README.md b/README.md index 8905ba6..052595d 100644 --- a/README.md +++ b/README.md @@ -204,6 +204,7 @@ Keep in mind that you need to put everything your test needed in the same functi - `SystemTable::current()` was replaced with `zfi::system_table()`. - `Image::current()` was replaced with `zfi::current_image()`. - All getters on `SystemTable` no longer return a static lifetime. +- `EfiStr` and `EfiString` to longer implement `Display`. Use `EfiStr::display()` instead. ## License diff --git a/src/string.rs b/src/string.rs index ccdb173..daf49a2 100644 --- a/src/string.rs +++ b/src/string.rs @@ -1,8 +1,7 @@ use alloc::borrow::ToOwned; -use alloc::string::String; use alloc::vec::Vec; use core::borrow::Borrow; -use core::fmt::{Display, Formatter}; +use core::fmt::{Formatter, Write}; use core::mem::transmute; use core::slice::{from_raw_parts, IterMut}; use core::str::FromStr; @@ -15,6 +14,7 @@ use core::str::FromStr; pub struct EfiStr([u16]); impl EfiStr { + /// An empty string with only NUL character. pub const EMPTY: &'static Self = unsafe { Self::new_unchecked(&[0]) }; /// # Safety @@ -40,17 +40,26 @@ impl EfiStr { Self::new_unchecked(from_raw_parts(ptr, len + 1)) } + /// Returnes a pointer to the first character. pub const fn as_ptr(&self) -> *const u16 { self.0.as_ptr() } + /// Returnes length of this string without NUL, in character. pub const fn len(&self) -> usize { self.0.len() - 1 } + /// Returns `true` if this string contains only NUL character. pub const fn is_empty(&self) -> bool { self.len() == 0 } + + /// Returns object that implement [`core::fmt::Display`] for safely printing string that may + /// contain non-Unicode data. + pub const fn display(&self) -> impl core::fmt::Display + '_ { + Display(self) + } } impl AsRef for EfiStr { @@ -83,12 +92,24 @@ impl ToOwned for EfiStr { } } -impl Display for EfiStr { +/// Provides [`core::fmt::Display`] to display [`EfiStr`] lossy. +struct Display<'a>(&'a EfiStr); + +impl<'a> core::fmt::Display for Display<'a> { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - let d = &self.0[..(self.0.len() - 1)]; // Exclude NUL. - let s = String::from_utf16(d).unwrap(); + // SAFETY: EfiStr guarantee to have NUL at the end. + let mut ptr = self.0.as_ptr(); + + while unsafe { *ptr != 0 } { + let c = unsafe { *ptr }; + let c = char::from_u32(c.into()).unwrap_or(char::REPLACEMENT_CHARACTER); + + f.write_char(c)?; + + unsafe { ptr = ptr.add(1) }; + } - f.write_str(&s) + Ok(()) } } @@ -166,12 +187,6 @@ impl Borrow for EfiString { } } -impl Display for EfiString { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - self.as_ref().fmt(f) - } -} - /// A non-NUL character in the EFI string. #[repr(transparent)] pub struct EfiChar(u16);