uefi/data_types/
strs.rs

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