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