• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use super::chars::{Char16, Char8, NUL_16, NUL_8};
2 use super::UnalignedSlice;
3 use crate::polyfill::maybe_uninit_slice_assume_init_ref;
4 use core::borrow::Borrow;
5 use core::ffi::CStr;
6 use core::fmt::{self, Display, Formatter};
7 use core::mem::MaybeUninit;
8 use core::slice;
9 
10 #[cfg(feature = "alloc")]
11 use super::CString16;
12 
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 {
fmt(&self, f: &mut Formatter<'_>) -> fmt::Result25     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.
38 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
39 pub enum FromSliceWithNulError {
40     /// An invalid character was encountered before the end of the slice
41     InvalidChar(usize),
42 
43     /// A null character was encountered before the end of the slice
44     InteriorNul(usize),
45 
46     /// The slice was not null-terminated
47     NotNulTerminated,
48 }
49 
50 impl Display for FromSliceWithNulError {
fmt(&self, f: &mut Formatter<'_>) -> fmt::Result51     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
52         match self {
53             Self::InvalidChar(usize) => write!(f, "invalid character at index {}", usize),
54             Self::InteriorNul(usize) => write!(f, "interior null character at index {}", usize),
55             Self::NotNulTerminated => write!(f, "not null-terminated"),
56         }
57     }
58 }
59 
60 #[cfg(feature = "unstable")]
61 impl core::error::Error for FromSliceWithNulError {}
62 
63 /// Error returned by [`CStr16::from_unaligned_slice`].
64 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
65 pub enum UnalignedCStr16Error {
66     /// An invalid character was encountered.
67     InvalidChar(usize),
68 
69     /// A null character was encountered before the end of the data.
70     InteriorNul(usize),
71 
72     /// The data was not null-terminated.
73     NotNulTerminated,
74 
75     /// The buffer is not big enough to hold the entire string and
76     /// trailing null character.
77     BufferTooSmall,
78 }
79 
80 impl Display for UnalignedCStr16Error {
fmt(&self, f: &mut Formatter<'_>) -> fmt::Result81     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
82         match self {
83             Self::InvalidChar(usize) => write!(f, "invalid character at index {}", usize),
84             Self::InteriorNul(usize) => write!(f, "interior null character at index {}", usize),
85             Self::NotNulTerminated => write!(f, "not null-terminated"),
86             Self::BufferTooSmall => write!(f, "buffer too small"),
87         }
88     }
89 }
90 
91 #[cfg(feature = "unstable")]
92 impl core::error::Error for UnalignedCStr16Error {}
93 
94 /// Error returned by [`CStr16::from_str_with_buf`].
95 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
96 pub enum FromStrWithBufError {
97     /// An invalid character was encountered before the end of the string
98     InvalidChar(usize),
99 
100     /// A null character was encountered in the string
101     InteriorNul(usize),
102 
103     /// The buffer is not big enough to hold the entire string and
104     /// trailing null character
105     BufferTooSmall,
106 }
107 
108 impl Display for FromStrWithBufError {
fmt(&self, f: &mut Formatter<'_>) -> fmt::Result109     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
110         match self {
111             Self::InvalidChar(usize) => write!(f, "invalid character at index {}", usize),
112             Self::InteriorNul(usize) => write!(f, "interior null character at index {}", usize),
113             Self::BufferTooSmall => write!(f, "buffer too small"),
114         }
115     }
116 }
117 
118 #[cfg(feature = "unstable")]
119 impl core::error::Error for FromStrWithBufError {}
120 
121 /// A null-terminated Latin-1 string.
122 ///
123 /// This type is largely inspired by [`core::ffi::CStr`] with the exception that all characters are
124 /// guaranteed to be 8 bit long.
125 ///
126 /// A [`CStr8`] can be constructed from a [`core::ffi::CStr`] via a `try_from` call:
127 /// ```ignore
128 /// let cstr8: &CStr8 = TryFrom::try_from(cstr).unwrap();
129 /// ```
130 ///
131 /// For convenience, a [`CStr8`] is comparable with [`core::str`] and
132 /// `alloc::string::String` from the standard library through the trait [`EqStrUntilNul`].
133 #[repr(transparent)]
134 #[derive(Eq, PartialEq, Ord, PartialOrd, Hash)]
135 pub struct CStr8([Char8]);
136 
137 impl CStr8 {
138     /// Takes a raw pointer to a null-terminated Latin-1 string and wraps it in a CStr8 reference.
139     ///
140     /// # Safety
141     ///
142     /// The function will start accessing memory from `ptr` until the first
143     /// null byte. It's the callers responsibility to ensure `ptr` points to
144     /// a valid null-terminated string in accessible memory.
145     #[must_use]
from_ptr<'ptr>(ptr: *const Char8) -> &'ptr Self146     pub unsafe fn from_ptr<'ptr>(ptr: *const Char8) -> &'ptr Self {
147         let mut len = 0;
148         while *ptr.add(len) != NUL_8 {
149             len += 1
150         }
151         let ptr = ptr.cast::<u8>();
152         Self::from_bytes_with_nul_unchecked(slice::from_raw_parts(ptr, len + 1))
153     }
154 
155     /// Creates a CStr8 reference from bytes.
from_bytes_with_nul(chars: &[u8]) -> Result<&Self, FromSliceWithNulError>156     pub fn from_bytes_with_nul(chars: &[u8]) -> Result<&Self, FromSliceWithNulError> {
157         let nul_pos = chars.iter().position(|&c| c == 0);
158         if let Some(nul_pos) = nul_pos {
159             if nul_pos + 1 != chars.len() {
160                 return Err(FromSliceWithNulError::InteriorNul(nul_pos));
161             }
162             Ok(unsafe { Self::from_bytes_with_nul_unchecked(chars) })
163         } else {
164             Err(FromSliceWithNulError::NotNulTerminated)
165         }
166     }
167 
168     /// Unsafely creates a CStr8 reference from bytes.
169     ///
170     /// # Safety
171     ///
172     /// It's the callers responsibility to ensure chars is a valid Latin-1
173     /// null-terminated string, with no interior null bytes.
174     #[must_use]
from_bytes_with_nul_unchecked(chars: &[u8]) -> &Self175     pub const unsafe fn from_bytes_with_nul_unchecked(chars: &[u8]) -> &Self {
176         &*(chars as *const [u8] as *const Self)
177     }
178 
179     /// Returns the inner pointer to this CStr8.
180     #[must_use]
as_ptr(&self) -> *const Char8181     pub const fn as_ptr(&self) -> *const Char8 {
182         self.0.as_ptr()
183     }
184 
185     /// Returns the underlying bytes as slice including the terminating null
186     /// character.
187     #[must_use]
as_bytes(&self) -> &[u8]188     pub const fn as_bytes(&self) -> &[u8] {
189         unsafe { &*(&self.0 as *const [Char8] as *const [u8]) }
190     }
191 }
192 
193 impl fmt::Debug for CStr8 {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result194     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
195         write!(f, "CStr8({:?})", &self.0)
196     }
197 }
198 
199 impl fmt::Display for CStr8 {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result200     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
201         for c in self.0.iter() {
202             <Char8 as fmt::Display>::fmt(c, f)?;
203         }
204         Ok(())
205     }
206 }
207 
208 impl AsRef<[u8]> for CStr8 {
as_ref(&self) -> &[u8]209     fn as_ref(&self) -> &[u8] {
210         self.as_bytes()
211     }
212 }
213 
214 impl Borrow<[u8]> for CStr8 {
borrow(&self) -> &[u8]215     fn borrow(&self) -> &[u8] {
216         self.as_bytes()
217     }
218 }
219 
220 impl<StrType: AsRef<str> + ?Sized> EqStrUntilNul<StrType> for CStr8 {
eq_str_until_nul(&self, other: &StrType) -> bool221     fn eq_str_until_nul(&self, other: &StrType) -> bool {
222         let other = other.as_ref();
223 
224         // TODO: CStr16 has .iter() implemented, CStr8 not yet
225         let any_not_equal = self
226             .0
227             .iter()
228             .copied()
229             .map(char::from)
230             .zip(other.chars())
231             // This only works as CStr8 is guaranteed to have a fixed character length
232             // (unlike UTF-8).
233             .take_while(|(l, r)| *l != '\0' && *r != '\0')
234             .any(|(l, r)| l != r);
235 
236         !any_not_equal
237     }
238 }
239 
240 impl<'a> TryFrom<&'a CStr> for &'a CStr8 {
241     type Error = FromSliceWithNulError;
242 
try_from(cstr: &'a CStr) -> Result<Self, Self::Error>243     fn try_from(cstr: &'a CStr) -> Result<Self, Self::Error> {
244         CStr8::from_bytes_with_nul(cstr.to_bytes_with_nul())
245     }
246 }
247 
248 /// Get a Latin-1 character from a UTF-8 byte slice at the given offset.
249 ///
250 /// Returns a pair containing the Latin-1 character and the number of bytes in
251 /// the UTF-8 encoding of that character.
252 ///
253 /// Panics if the string cannot be encoded in Latin-1.
254 ///
255 /// # Safety
256 ///
257 /// The input `bytes` must be valid UTF-8.
latin1_from_utf8_at_offset(bytes: &[u8], offset: usize) -> (u8, usize)258 const unsafe fn latin1_from_utf8_at_offset(bytes: &[u8], offset: usize) -> (u8, usize) {
259     if bytes[offset] & 0b1000_0000 == 0b0000_0000 {
260         (bytes[offset], 1)
261     } else if bytes[offset] & 0b1110_0000 == 0b1100_0000 {
262         let a = (bytes[offset] & 0b0001_1111) as u16;
263         let b = (bytes[offset + 1] & 0b0011_1111) as u16;
264         let ch = a << 6 | b;
265         if ch > 0xff {
266             panic!("input string cannot be encoded as Latin-1");
267         }
268         (ch as u8, 2)
269     } else {
270         // Latin-1 code points only go up to 0xff, so if the input contains any
271         // UTF-8 characters larger than two bytes it cannot be converted to
272         // Latin-1.
273         panic!("input string cannot be encoded as Latin-1");
274     }
275 }
276 
277 /// Count the number of Latin-1 characters in a string.
278 ///
279 /// Panics if the string cannot be encoded in Latin-1.
280 ///
281 /// This is public but hidden; it is used in the `cstr8` macro.
282 #[must_use]
str_num_latin1_chars(s: &str) -> usize283 pub const fn str_num_latin1_chars(s: &str) -> usize {
284     let bytes = s.as_bytes();
285     let len = bytes.len();
286 
287     let mut offset = 0;
288     let mut num_latin1_chars = 0;
289 
290     while offset < len {
291         // SAFETY: `bytes` is valid UTF-8.
292         let (_, num_utf8_bytes) = unsafe { latin1_from_utf8_at_offset(bytes, offset) };
293         offset += num_utf8_bytes;
294         num_latin1_chars += 1;
295     }
296 
297     num_latin1_chars
298 }
299 
300 /// Convert a `str` into a null-terminated Latin-1 character array.
301 ///
302 /// Panics if the string cannot be encoded in Latin-1.
303 ///
304 /// This is public but hidden; it is used in the `cstr8` macro.
305 #[must_use]
str_to_latin1<const N: usize>(s: &str) -> [u8; N]306 pub const fn str_to_latin1<const N: usize>(s: &str) -> [u8; N] {
307     let bytes = s.as_bytes();
308     let len = bytes.len();
309 
310     let mut output = [0; N];
311 
312     let mut output_offset = 0;
313     let mut input_offset = 0;
314     while input_offset < len {
315         // SAFETY: `bytes` is valid UTF-8.
316         let (ch, num_utf8_bytes) = unsafe { latin1_from_utf8_at_offset(bytes, input_offset) };
317         if ch == 0 {
318             panic!("interior null character");
319         } else {
320             output[output_offset] = ch;
321             output_offset += 1;
322             input_offset += num_utf8_bytes;
323         }
324     }
325 
326     // The output array must be one bigger than the converted string,
327     // to leave room for the trailing null character.
328     if output_offset + 1 != N {
329         panic!("incorrect array length");
330     }
331 
332     output
333 }
334 
335 /// An UCS-2 null-terminated string slice.
336 ///
337 /// This type is largely inspired by [`core::ffi::CStr`] with the exception that all characters are
338 /// guaranteed to be 16 bit long.
339 ///
340 /// For convenience, a [`CStr16`] is comparable with [`core::str`] and
341 /// `alloc::string::String` from the standard library through the trait [`EqStrUntilNul`].
342 #[derive(Eq, PartialEq, Ord, PartialOrd, Hash)]
343 #[repr(transparent)]
344 pub struct CStr16([Char16]);
345 
346 impl CStr16 {
347     /// Wraps a raw UEFI string with a safe C string wrapper
348     ///
349     /// # Safety
350     ///
351     /// The function will start accessing memory from `ptr` until the first
352     /// null character. It's the callers responsibility to ensure `ptr` points to
353     /// a valid string, in accessible memory.
354     #[must_use]
from_ptr<'ptr>(ptr: *const Char16) -> &'ptr Self355     pub unsafe fn from_ptr<'ptr>(ptr: *const Char16) -> &'ptr Self {
356         let mut len = 0;
357         while *ptr.add(len) != NUL_16 {
358             len += 1
359         }
360         let ptr = ptr.cast::<u16>();
361         Self::from_u16_with_nul_unchecked(slice::from_raw_parts(ptr, len + 1))
362     }
363 
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.
from_u16_until_nul(codes: &[u16]) -> Result<&Self, FromSliceUntilNulError>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 
381     /// Creates a `&CStr16` from a u16 slice, if the slice contains exactly
382     /// one terminating null-byte and all chars are valid UCS-2 chars.
from_u16_with_nul(codes: &[u16]) -> Result<&Self, FromSliceWithNulError>383     pub fn from_u16_with_nul(codes: &[u16]) -> Result<&Self, FromSliceWithNulError> {
384         for (pos, &code) in codes.iter().enumerate() {
385             match code.try_into() {
386                 Ok(NUL_16) => {
387                     if pos != codes.len() - 1 {
388                         return Err(FromSliceWithNulError::InteriorNul(pos));
389                     } else {
390                         return Ok(unsafe { Self::from_u16_with_nul_unchecked(codes) });
391                     }
392                 }
393                 Err(_) => {
394                     return Err(FromSliceWithNulError::InvalidChar(pos));
395                 }
396                 _ => {}
397             }
398         }
399         Err(FromSliceWithNulError::NotNulTerminated)
400     }
401 
402     /// Unsafely creates a `&CStr16` from a u16 slice.
403     ///
404     /// # Safety
405     ///
406     /// It's the callers responsibility to ensure chars is a valid UCS-2
407     /// null-terminated string, with no interior null characters.
408     #[must_use]
from_u16_with_nul_unchecked(codes: &[u16]) -> &Self409     pub const unsafe fn from_u16_with_nul_unchecked(codes: &[u16]) -> &Self {
410         &*(codes as *const [u16] as *const Self)
411     }
412 
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.
from_char16_until_nul(chars: &[Char16]) -> Result<&Self, FromSliceUntilNulError>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 
429     /// Creates a `&CStr16` from a [`Char16`] slice, if the slice is
430     /// null-terminated and has no interior null characters.
from_char16_with_nul(chars: &[Char16]) -> Result<&Self, FromSliceWithNulError>431     pub fn from_char16_with_nul(chars: &[Char16]) -> Result<&Self, FromSliceWithNulError> {
432         // Fail early if the input is empty.
433         if chars.is_empty() {
434             return Err(FromSliceWithNulError::NotNulTerminated);
435         }
436 
437         // Find the index of the first null char.
438         if let Some(null_index) = chars.iter().position(|c| *c == NUL_16) {
439             // Verify the null character is at the end.
440             if null_index == chars.len() - 1 {
441                 // Safety: the input is null-terminated and has no interior nulls.
442                 Ok(unsafe { Self::from_char16_with_nul_unchecked(chars) })
443             } else {
444                 Err(FromSliceWithNulError::InteriorNul(null_index))
445             }
446         } else {
447             Err(FromSliceWithNulError::NotNulTerminated)
448         }
449     }
450 
451     /// Unsafely creates a `&CStr16` from a `Char16` slice.
452     ///
453     /// # Safety
454     ///
455     /// It's the callers responsibility to ensure chars is null-terminated and
456     /// has no interior null characters.
457     #[must_use]
from_char16_with_nul_unchecked(chars: &[Char16]) -> &Self458     pub const unsafe fn from_char16_with_nul_unchecked(chars: &[Char16]) -> &Self {
459         let ptr: *const [Char16] = chars;
460         &*(ptr as *const Self)
461     }
462 
463     /// Convert a [`&str`] to a `&CStr16`, backed by a buffer.
464     ///
465     /// The input string must contain only characters representable with
466     /// UCS-2, and must not contain any null characters (even at the end of
467     /// the input).
468     ///
469     /// The backing buffer must be big enough to hold the converted string as
470     /// well as a trailing null character.
471     ///
472     /// # Examples
473     ///
474     /// Convert the UTF-8 string "ABC" to a `&CStr16`:
475     ///
476     /// ```
477     /// use uefi::CStr16;
478     ///
479     /// let mut buf = [0; 4];
480     /// CStr16::from_str_with_buf("ABC", &mut buf).unwrap();
481     /// ```
from_str_with_buf<'a>( input: &str, buf: &'a mut [u16], ) -> Result<&'a Self, FromStrWithBufError>482     pub fn from_str_with_buf<'a>(
483         input: &str,
484         buf: &'a mut [u16],
485     ) -> Result<&'a Self, FromStrWithBufError> {
486         let mut index = 0;
487 
488         // Convert to UTF-16.
489         for c in input.encode_utf16() {
490             *buf.get_mut(index)
491                 .ok_or(FromStrWithBufError::BufferTooSmall)? = c;
492             index += 1;
493         }
494 
495         // Add trailing null character.
496         *buf.get_mut(index)
497             .ok_or(FromStrWithBufError::BufferTooSmall)? = 0;
498 
499         // Convert from u16 to Char16. This checks for invalid UCS-2 chars and
500         // interior nulls. The NotNulTerminated case is unreachable because we
501         // just added a trailing null character.
502         Self::from_u16_with_nul(&buf[..index + 1]).map_err(|err| match err {
503             FromSliceWithNulError::InvalidChar(p) => FromStrWithBufError::InvalidChar(p),
504             FromSliceWithNulError::InteriorNul(p) => FromStrWithBufError::InteriorNul(p),
505             FromSliceWithNulError::NotNulTerminated => {
506                 unreachable!()
507             }
508         })
509     }
510 
511     /// Create a `&CStr16` from an [`UnalignedSlice`] using an aligned
512     /// buffer for storage. The lifetime of the output is tied to `buf`,
513     /// not `src`.
from_unaligned_slice<'buf>( src: &UnalignedSlice<'_, u16>, buf: &'buf mut [MaybeUninit<u16>], ) -> Result<&'buf Self, UnalignedCStr16Error>514     pub fn from_unaligned_slice<'buf>(
515         src: &UnalignedSlice<'_, u16>,
516         buf: &'buf mut [MaybeUninit<u16>],
517     ) -> Result<&'buf Self, UnalignedCStr16Error> {
518         // The input `buf` might be longer than needed, so get a
519         // subslice of the required length.
520         let buf = buf
521             .get_mut(..src.len())
522             .ok_or(UnalignedCStr16Error::BufferTooSmall)?;
523 
524         src.copy_to_maybe_uninit(buf);
525         let buf = unsafe {
526             // Safety: `copy_buf` fully initializes the slice.
527             maybe_uninit_slice_assume_init_ref(buf)
528         };
529         Self::from_u16_with_nul(buf).map_err(|e| match e {
530             FromSliceWithNulError::InvalidChar(v) => UnalignedCStr16Error::InvalidChar(v),
531             FromSliceWithNulError::InteriorNul(v) => UnalignedCStr16Error::InteriorNul(v),
532             FromSliceWithNulError::NotNulTerminated => UnalignedCStr16Error::NotNulTerminated,
533         })
534     }
535 
536     /// Returns the inner pointer to this C16 string.
537     #[must_use]
as_ptr(&self) -> *const Char16538     pub const fn as_ptr(&self) -> *const Char16 {
539         self.0.as_ptr()
540     }
541 
542     /// Get the underlying [`Char16`]s as slice without the trailing null.
543     #[must_use]
as_slice(&self) -> &[Char16]544     pub fn as_slice(&self) -> &[Char16] {
545         &self.0[..self.num_chars()]
546     }
547 
548     /// Get the underlying [`Char16`]s as slice including the trailing null.
549     #[must_use]
as_slice_with_nul(&self) -> &[Char16]550     pub const fn as_slice_with_nul(&self) -> &[Char16] {
551         &self.0
552     }
553 
554     /// Converts this C string to a u16 slice without the trailing null.
555     #[must_use]
to_u16_slice(&self) -> &[u16]556     pub fn to_u16_slice(&self) -> &[u16] {
557         let chars = self.to_u16_slice_with_nul();
558         &chars[..chars.len() - 1]
559     }
560 
561     /// Converts this C string to a u16 slice containing the trailing null.
562     #[must_use]
to_u16_slice_with_nul(&self) -> &[u16]563     pub const fn to_u16_slice_with_nul(&self) -> &[u16] {
564         unsafe { &*(&self.0 as *const [Char16] as *const [u16]) }
565     }
566 
567     /// Returns an iterator over this C string
568     #[must_use]
iter(&self) -> CStr16Iter569     pub const fn iter(&self) -> CStr16Iter {
570         CStr16Iter {
571             inner: self,
572             pos: 0,
573         }
574     }
575 
576     /// Returns the number of characters without the trailing null. character
577     #[must_use]
num_chars(&self) -> usize578     pub const fn num_chars(&self) -> usize {
579         self.0.len() - 1
580     }
581 
582     /// Returns if the string is empty. This ignores the null character.
583     #[must_use]
is_empty(&self) -> bool584     pub const fn is_empty(&self) -> bool {
585         self.num_chars() == 0
586     }
587 
588     /// Get the number of bytes in the string (including the trailing null).
589     #[must_use]
num_bytes(&self) -> usize590     pub const fn num_bytes(&self) -> usize {
591         self.0.len() * 2
592     }
593 
594     /// Checks if all characters in this string are within the ASCII range.
595     #[must_use]
is_ascii(&self) -> bool596     pub fn is_ascii(&self) -> bool {
597         self.0.iter().all(|c| c.is_ascii())
598     }
599 
600     /// Writes each [`Char16`] as a [`char`] (4 bytes long in Rust language) into the buffer.
601     /// It is up to the implementer of [`core::fmt::Write`] to convert the char to a string
602     /// with proper encoding/charset. For example, in the case of [`alloc::string::String`]
603     /// all Rust chars (UTF-32) get converted to UTF-8.
604     ///
605     /// ## Example
606     ///
607     /// ```ignore
608     /// let firmware_vendor_c16_str: CStr16 = ...;
609     /// // crate "arrayvec" uses stack-allocated arrays for Strings => no heap allocations
610     /// let mut buf = arrayvec::ArrayString::<128>::new();
611     /// firmware_vendor_c16_str.as_str_in_buf(&mut buf);
612     /// log::info!("as rust str: {}", buf.as_str());
613     /// ```
614     ///
615     /// [`alloc::string::String`]: https://doc.rust-lang.org/nightly/alloc/string/struct.String.html
as_str_in_buf(&self, buf: &mut dyn core::fmt::Write) -> core::fmt::Result616     pub fn as_str_in_buf(&self, buf: &mut dyn core::fmt::Write) -> core::fmt::Result {
617         for c16 in self.iter() {
618             buf.write_char(char::from(*c16))?;
619         }
620         Ok(())
621     }
622 
623     /// Returns the underlying bytes as slice including the terminating null
624     /// character.
625     #[must_use]
as_bytes(&self) -> &[u8]626     pub const fn as_bytes(&self) -> &[u8] {
627         unsafe { slice::from_raw_parts(self.0.as_ptr().cast(), self.num_bytes()) }
628     }
629 }
630 
631 impl AsRef<[u8]> for CStr16 {
as_ref(&self) -> &[u8]632     fn as_ref(&self) -> &[u8] {
633         self.as_bytes()
634     }
635 }
636 
637 impl Borrow<[u8]> for CStr16 {
borrow(&self) -> &[u8]638     fn borrow(&self) -> &[u8] {
639         self.as_bytes()
640     }
641 }
642 
643 #[cfg(feature = "alloc")]
644 impl From<&CStr16> for alloc::string::String {
from(value: &CStr16) -> Self645     fn from(value: &CStr16) -> Self {
646         value
647             .as_slice()
648             .iter()
649             .copied()
650             .map(u16::from)
651             .map(u32::from)
652             .map(|int| char::from_u32(int).expect("Should be encodable as UTF-8"))
653             .collect::<Self>()
654     }
655 }
656 
657 impl<StrType: AsRef<str> + ?Sized> EqStrUntilNul<StrType> for CStr16 {
eq_str_until_nul(&self, other: &StrType) -> bool658     fn eq_str_until_nul(&self, other: &StrType) -> bool {
659         let other = other.as_ref();
660 
661         let any_not_equal = self
662             .iter()
663             .copied()
664             .map(char::from)
665             .zip(other.chars())
666             // This only works as CStr16 is guaranteed to have a fixed character length
667             // (unlike UTF-8 or UTF-16).
668             .take_while(|(l, r)| *l != '\0' && *r != '\0')
669             .any(|(l, r)| l != r);
670 
671         !any_not_equal
672     }
673 }
674 
675 impl AsRef<Self> for CStr16 {
as_ref(&self) -> &Self676     fn as_ref(&self) -> &Self {
677         self
678     }
679 }
680 
681 /// An iterator over the [`Char16`]s in a [`CStr16`].
682 #[derive(Debug)]
683 pub struct CStr16Iter<'a> {
684     inner: &'a CStr16,
685     pos: usize,
686 }
687 
688 impl<'a> Iterator for CStr16Iter<'a> {
689     type Item = &'a Char16;
690 
next(&mut self) -> Option<Self::Item>691     fn next(&mut self) -> Option<Self::Item> {
692         if self.pos >= self.inner.0.len() - 1 {
693             None
694         } else {
695             self.pos += 1;
696             self.inner.0.get(self.pos - 1)
697         }
698     }
699 }
700 
701 impl fmt::Debug for CStr16 {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result702     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
703         write!(f, "CStr16({:?})", &self.0)
704     }
705 }
706 
707 impl fmt::Display for CStr16 {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result708     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
709         for c in self.iter() {
710             <Char16 as fmt::Display>::fmt(c, f)?;
711         }
712         Ok(())
713     }
714 }
715 
716 #[cfg(feature = "alloc")]
717 impl PartialEq<CString16> for &CStr16 {
eq(&self, other: &CString16) -> bool718     fn eq(&self, other: &CString16) -> bool {
719         PartialEq::eq(*self, other.as_ref())
720     }
721 }
722 
723 impl<'a> UnalignedSlice<'a, u16> {
724     /// Create a [`CStr16`] from an [`UnalignedSlice`] using an aligned
725     /// buffer for storage. The lifetime of the output is tied to `buf`,
726     /// not `self`.
to_cstr16<'buf>( &self, buf: &'buf mut [MaybeUninit<u16>], ) -> Result<&'buf CStr16, UnalignedCStr16Error>727     pub fn to_cstr16<'buf>(
728         &self,
729         buf: &'buf mut [MaybeUninit<u16>],
730     ) -> Result<&'buf CStr16, UnalignedCStr16Error> {
731         CStr16::from_unaligned_slice(self, buf)
732     }
733 }
734 
735 /// The EqStrUntilNul trait helps to compare Rust strings against UEFI string types (UCS-2 strings).
736 /// The given generic implementation of this trait enables us that we only have to
737 /// implement one direction (`left.eq_str_until_nul(&right)`) for each UEFI string type and we
738 /// get the other direction (`right.eq_str_until_nul(&left)`) for free. Hence, the relation is
739 /// reflexive.
740 pub trait EqStrUntilNul<StrType: ?Sized> {
741     /// Checks if the provided Rust string `StrType` is equal to [Self] until the first null character
742     /// is found. An exception is the terminating null character of [Self] which is ignored.
743     ///
744     /// As soon as the first null character in either `&self` or `other` is found, this method returns.
745     /// Note that Rust strings are allowed to contain null bytes that do not terminate the string.
746     /// Although this is rather unusual, you can compare `"foo\0bar"` with an instance of [Self].
747     /// In that case, only `foo"` is compared against [Self] (if [Self] is long enough).
eq_str_until_nul(&self, other: &StrType) -> bool748     fn eq_str_until_nul(&self, other: &StrType) -> bool;
749 }
750 
751 // magic implementation which transforms an existing `left.eq_str_until_nul(&right)` implementation
752 // into an additional working `right.eq_str_until_nul(&left)` implementation.
753 impl<StrType, C16StrType> EqStrUntilNul<C16StrType> for StrType
754 where
755     StrType: AsRef<str>,
756     C16StrType: EqStrUntilNul<StrType> + ?Sized,
757 {
eq_str_until_nul(&self, other: &C16StrType) -> bool758     fn eq_str_until_nul(&self, other: &C16StrType) -> bool {
759         // reuse the existing implementation
760         other.eq_str_until_nul(self)
761     }
762 }
763 
764 #[cfg(test)]
765 mod tests {
766     use super::*;
767     use crate::{cstr16, cstr8};
768     use alloc::string::String;
769 
770     // Tests if our CStr8 type can be constructed from a valid core::ffi::CStr
771     #[test]
test_cstr8_from_cstr()772     fn test_cstr8_from_cstr() {
773         let msg = "hello world\0";
774         let cstr = unsafe { CStr::from_ptr(msg.as_ptr().cast()) };
775         let cstr8: &CStr8 = TryFrom::try_from(cstr).unwrap();
776         assert!(cstr8.eq_str_until_nul(msg));
777         assert!(msg.eq_str_until_nul(cstr8));
778     }
779 
780     #[test]
test_cstr8_as_bytes()781     fn test_cstr8_as_bytes() {
782         let string: &CStr8 = cstr8!("a");
783         assert_eq!(string.as_bytes(), &[b'a', 0]);
784         assert_eq!(<CStr8 as AsRef<[u8]>>::as_ref(string), &[b'a', 0]);
785         assert_eq!(<CStr8 as Borrow<[u8]>>::borrow(string), &[b'a', 0]);
786     }
787 
788     #[test]
test_cstr16_num_bytes()789     fn test_cstr16_num_bytes() {
790         let s = CStr16::from_u16_with_nul(&[65, 66, 67, 0]).unwrap();
791         assert_eq!(s.num_bytes(), 8);
792     }
793 
794     #[test]
test_cstr16_from_u16_until_nul()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]
test_cstr16_from_char16_until_nul()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 
863     #[test]
test_cstr16_from_char16_with_nul()864     fn test_cstr16_from_char16_with_nul() {
865         // Invalid: empty input.
866         assert_eq!(
867             CStr16::from_char16_with_nul(&[]),
868             Err(FromSliceWithNulError::NotNulTerminated)
869         );
870 
871         // Invalid: interior null.
872         assert_eq!(
873             CStr16::from_char16_with_nul(&[
874                 Char16::try_from('a').unwrap(),
875                 NUL_16,
876                 Char16::try_from('b').unwrap(),
877                 NUL_16
878             ]),
879             Err(FromSliceWithNulError::InteriorNul(1))
880         );
881 
882         // Invalid: no trailing null.
883         assert_eq!(
884             CStr16::from_char16_with_nul(&[
885                 Char16::try_from('a').unwrap(),
886                 Char16::try_from('b').unwrap(),
887             ]),
888             Err(FromSliceWithNulError::NotNulTerminated)
889         );
890 
891         // Valid.
892         assert_eq!(
893             CStr16::from_char16_with_nul(&[
894                 Char16::try_from('a').unwrap(),
895                 Char16::try_from('b').unwrap(),
896                 NUL_16,
897             ]),
898             Ok(cstr16!("ab"))
899         );
900     }
901 
902     #[test]
test_cstr16_from_str_with_buf()903     fn test_cstr16_from_str_with_buf() {
904         let mut buf = [0; 4];
905 
906         // OK: buf is exactly the right size.
907         let s = CStr16::from_str_with_buf("ABC", &mut buf).unwrap();
908         assert_eq!(s.to_u16_slice_with_nul(), [65, 66, 67, 0]);
909 
910         // OK: buf is bigger than needed.
911         let s = CStr16::from_str_with_buf("A", &mut buf).unwrap();
912         assert_eq!(s.to_u16_slice_with_nul(), [65, 0]);
913 
914         // Error: buf is too small.
915         assert_eq!(
916             CStr16::from_str_with_buf("ABCD", &mut buf).unwrap_err(),
917             FromStrWithBufError::BufferTooSmall
918         );
919 
920         // Error: invalid character.
921         assert_eq!(
922             CStr16::from_str_with_buf("a��", &mut buf).unwrap_err(),
923             FromStrWithBufError::InvalidChar(1),
924         );
925 
926         // Error: interior null.
927         assert_eq!(
928             CStr16::from_str_with_buf("a\0b", &mut buf).unwrap_err(),
929             FromStrWithBufError::InteriorNul(1),
930         );
931     }
932 
933     #[test]
test_cstr16_macro()934     fn test_cstr16_macro() {
935         // Just a sanity check to make sure it's spitting out the right characters
936         assert_eq!(
937             crate::prelude::cstr16!("ABC").to_u16_slice_with_nul(),
938             [65, 66, 67, 0]
939         )
940     }
941 
942     #[test]
test_unaligned_cstr16()943     fn test_unaligned_cstr16() {
944         let mut buf = [0u16; 6];
945         let us = unsafe {
946             let ptr = buf.as_mut_ptr() as *mut u8;
947             // Intentionally create an unaligned u16 pointer. This
948             // leaves room for five u16 characters.
949             let ptr = ptr.add(1) as *mut u16;
950             // Write out the "test" string.
951             ptr.add(0).write_unaligned(b't'.into());
952             ptr.add(1).write_unaligned(b'e'.into());
953             ptr.add(2).write_unaligned(b's'.into());
954             ptr.add(3).write_unaligned(b't'.into());
955             ptr.add(4).write_unaligned(b'\0'.into());
956 
957             // Create the `UnalignedSlice`.
958             UnalignedSlice::new(ptr, 5)
959         };
960 
961         // Test `to_cstr16()` with too small of a buffer.
962         let mut buf = [MaybeUninit::new(0); 4];
963         assert_eq!(
964             us.to_cstr16(&mut buf).unwrap_err(),
965             UnalignedCStr16Error::BufferTooSmall
966         );
967         // Test with a big enough buffer.
968         let mut buf = [MaybeUninit::new(0); 5];
969         assert_eq!(
970             us.to_cstr16(&mut buf).unwrap(),
971             CString16::try_from("test").unwrap()
972         );
973 
974         // Test `to_cstring16()`.
975         assert_eq!(
976             us.to_cstring16().unwrap(),
977             CString16::try_from("test").unwrap()
978         );
979     }
980 
981     #[test]
test_cstr16_as_slice()982     fn test_cstr16_as_slice() {
983         let string: &CStr16 = cstr16!("a");
984         assert_eq!(string.as_slice(), &[Char16::try_from('a').unwrap()]);
985         assert_eq!(
986             string.as_slice_with_nul(),
987             &[Char16::try_from('a').unwrap(), NUL_16]
988         );
989     }
990 
991     #[test]
test_cstr16_as_bytes()992     fn test_cstr16_as_bytes() {
993         let string: &CStr16 = cstr16!("a");
994         assert_eq!(string.as_bytes(), &[b'a', 0, 0, 0]);
995         assert_eq!(<CStr16 as AsRef<[u8]>>::as_ref(string), &[b'a', 0, 0, 0]);
996         assert_eq!(<CStr16 as Borrow<[u8]>>::borrow(string), &[b'a', 0, 0, 0]);
997     }
998 
999     // Code generation helper for the compare tests of our CStrX types against "str" and "String"
1000     // from the standard library.
1001     #[allow(non_snake_case)]
1002     macro_rules! test_compare_cstrX {
1003         ($input:ident) => {
1004             assert!($input.eq_str_until_nul(&"test"));
1005             assert!($input.eq_str_until_nul(&String::from("test")));
1006 
1007             // now other direction
1008             assert!(String::from("test").eq_str_until_nul($input));
1009             assert!("test".eq_str_until_nul($input));
1010 
1011             // some more tests
1012             // this is fine: compare until the first null
1013             assert!($input.eq_str_until_nul(&"te\0st"));
1014             // this is fine
1015             assert!($input.eq_str_until_nul(&"test\0"));
1016             assert!(!$input.eq_str_until_nul(&"hello"));
1017         };
1018     }
1019 
1020     #[test]
test_compare_cstr8()1021     fn test_compare_cstr8() {
1022         // test various comparisons with different order (left, right)
1023         let input: &CStr8 = cstr8!("test");
1024         test_compare_cstrX!(input);
1025     }
1026 
1027     #[test]
test_compare_cstr16()1028     fn test_compare_cstr16() {
1029         let input: &CStr16 = cstr16!("test");
1030         test_compare_cstrX!(input);
1031     }
1032 
1033     /// Test that the `cstr16!` macro can be used in a `const` context.
1034     #[test]
test_cstr16_macro_const()1035     fn test_cstr16_macro_const() {
1036         const S: &CStr16 = cstr16!("ABC");
1037         assert_eq!(S.to_u16_slice_with_nul(), [65, 66, 67, 0]);
1038     }
1039 
1040     /// Tests the trait implementation of trait [`EqStrUntilNul]` for [`CStr8`].
1041     ///
1042     /// This tests that `String` and `str` from the standard library can be
1043     /// checked for equality against a [`CStr8`]. It checks both directions,
1044     /// i.e., the equality is reflexive.
1045     #[test]
test_cstr8_eq_std_str()1046     fn test_cstr8_eq_std_str() {
1047         let input: &CStr8 = cstr8!("test");
1048 
1049         // test various comparisons with different order (left, right)
1050         assert!(input.eq_str_until_nul("test")); // requires ?Sized constraint
1051         assert!(input.eq_str_until_nul(&"test"));
1052         assert!(input.eq_str_until_nul(&String::from("test")));
1053 
1054         // now other direction
1055         assert!(String::from("test").eq_str_until_nul(input));
1056         assert!("test".eq_str_until_nul(input));
1057     }
1058 
1059     /// Tests the trait implementation of trait [`EqStrUntilNul]` for [`CStr16`].
1060     ///
1061     /// This tests that `String` and `str` from the standard library can be
1062     /// checked for equality against a [`CStr16`]. It checks both directions,
1063     /// i.e., the equality is reflexive.
1064     #[test]
test_cstr16_eq_std_str()1065     fn test_cstr16_eq_std_str() {
1066         let input: &CStr16 = cstr16!("test");
1067 
1068         assert!(input.eq_str_until_nul("test")); // requires ?Sized constraint
1069         assert!(input.eq_str_until_nul(&"test"));
1070         assert!(input.eq_str_until_nul(&String::from("test")));
1071 
1072         // now other direction
1073         assert!(String::from("test").eq_str_until_nul(input));
1074         assert!("test".eq_str_until_nul(input));
1075     }
1076 }
1077