1use super::chars::{Char16, Char8, NUL_16, NUL_8};
2use super::UnalignedSlice;
3use crate::polyfill::maybe_uninit_slice_assume_init_ref;
4use core::ffi::CStr;
5use core::fmt;
6use core::iter::Iterator;
7use core::mem::MaybeUninit;
8use core::result::Result;
9use core::slice;
10
11#[cfg(feature = "alloc")]
12use super::CString16;
13
14/// Errors which can occur during checked `[uN]` -> `CStrN` conversions
15#[derive(Clone, Copy, Debug, Eq, PartialEq)]
16pub enum FromSliceWithNulError {
17 /// An invalid character was encountered before the end of the slice
18 InvalidChar(usize),
19
20 /// A null character was encountered before the end of the slice
21 InteriorNul(usize),
22
23 /// The slice was not null-terminated
24 NotNulTerminated,
25}
26
27/// Error returned by [`CStr16::from_unaligned_slice`].
28#[derive(Clone, Copy, Debug, Eq, PartialEq)]
29pub enum UnalignedCStr16Error {
30 /// An invalid character was encountered.
31 InvalidChar(usize),
32
33 /// A null character was encountered before the end of the data.
34 InteriorNul(usize),
35
36 /// The data was not null-terminated.
37 NotNulTerminated,
38
39 /// The buffer is not big enough to hold the entire string and
40 /// trailing null character.
41 BufferTooSmall,
42}
43
44/// Error returned by [`CStr16::from_str_with_buf`].
45#[derive(Clone, Copy, Debug, Eq, PartialEq)]
46pub enum FromStrWithBufError {
47 /// An invalid character was encountered before the end of the string
48 InvalidChar(usize),
49
50 /// A null character was encountered in the string
51 InteriorNul(usize),
52
53 /// The buffer is not big enough to hold the entire string and
54 /// trailing null character
55 BufferTooSmall,
56}
57
58/// A null-terminated Latin-1 string.
59///
60/// This type is largely inspired by [`core::ffi::CStr`] with the exception that all characters are
61/// guaranteed to be 8 bit long.
62///
63/// A [`CStr8`] can be constructed from a [`core::ffi::CStr`] via a `try_from` call:
64/// ```ignore
65/// let cstr8: &CStr8 = TryFrom::try_from(cstr).unwrap();
66/// ```
67///
68/// For convenience, a [`CStr8`] is comparable with [`core::str`] and
69/// `alloc::string::String` from the standard library through the trait [`EqStrUntilNul`].
70#[repr(transparent)]
71#[derive(Eq, PartialEq)]
72pub struct CStr8([Char8]);
73
74impl CStr8 {
75 /// Takes a raw pointer to a null-terminated Latin-1 string and wraps it in a CStr8 reference.
76 ///
77 /// # Safety
78 ///
79 /// The function will start accessing memory from `ptr` until the first
80 /// null byte. It's the callers responsibility to ensure `ptr` points to
81 /// a valid null-terminated string in accessible memory.
82 #[must_use]
83 pub unsafe fn from_ptr<'ptr>(ptr: *const Char8) -> &'ptr Self {
84 let mut len = 0;
85 while *ptr.add(len) != NUL_8 {
86 len += 1
87 }
88 let ptr = ptr.cast::<u8>();
89 Self::from_bytes_with_nul_unchecked(slice::from_raw_parts(ptr, len + 1))
90 }
91
92 /// Creates a CStr8 reference from bytes.
93 pub fn from_bytes_with_nul(chars: &[u8]) -> Result<&Self, FromSliceWithNulError> {
94 let nul_pos = chars.iter().position(|&c| c == 0);
95 if let Some(nul_pos) = nul_pos {
96 if nul_pos + 1 != chars.len() {
97 return Err(FromSliceWithNulError::InteriorNul(nul_pos));
98 }
99 Ok(unsafe { Self::from_bytes_with_nul_unchecked(chars) })
100 } else {
101 Err(FromSliceWithNulError::NotNulTerminated)
102 }
103 }
104
105 /// Unsafely creates a CStr8 reference from bytes.
106 ///
107 /// # Safety
108 ///
109 /// It's the callers responsibility to ensure chars is a valid Latin-1
110 /// null-terminated string, with no interior null bytes.
111 #[must_use]
112 pub const unsafe fn from_bytes_with_nul_unchecked(chars: &[u8]) -> &Self {
113 &*(chars as *const [u8] as *const Self)
114 }
115
116 /// Returns the inner pointer to this CStr8.
117 #[must_use]
118 pub const fn as_ptr(&self) -> *const Char8 {
119 self.0.as_ptr()
120 }
121
122 /// Converts this CStr8 to a slice of bytes without the terminating null byte.
123 #[must_use]
124 pub fn to_bytes(&self) -> &[u8] {
125 let chars = self.to_bytes_with_nul();
126 &chars[..chars.len() - 1]
127 }
128
129 /// Converts this CStr8 to a slice of bytes containing the trailing null byte.
130 #[must_use]
131 pub const fn to_bytes_with_nul(&self) -> &[u8] {
132 unsafe { &*(&self.0 as *const [Char8] as *const [u8]) }
133 }
134}
135
136impl fmt::Debug for CStr8 {
137 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
138 write!(f, "CStr8({:?})", &self.0)
139 }
140}
141
142impl fmt::Display for CStr8 {
143 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
144 for c: &Char8 in self.0.iter() {
145 <Char8 as fmt::Display>::fmt(self:c, f)?;
146 }
147 Ok(())
148 }
149}
150
151impl<StrType: AsRef<str> + ?Sized> EqStrUntilNul<StrType> for CStr8 {
152 fn eq_str_until_nul(&self, other: &StrType) -> bool {
153 let other: &str = other.as_ref();
154
155 // TODO: CStr16 has .iter() implemented, CStr8 not yet
156 let any_not_equal: bool = self
157 .0
158 .iter()
159 .copied()
160 .map(char::from)
161 .zip(other.chars())
162 // This only works as CStr8 is guaranteed to have a fixed character length
163 // (unlike UTF-8).
164 .take_while(|(l: &char, r: &char)| *l != '\0' && *r != '\0')
165 .any(|(l: char, r: char)| l != r);
166
167 !any_not_equal
168 }
169}
170
171impl<'a> TryFrom<&'a CStr> for &'a CStr8 {
172 type Error = FromSliceWithNulError;
173
174 fn try_from(cstr: &'a CStr) -> Result<Self, Self::Error> {
175 CStr8::from_bytes_with_nul(chars:cstr.to_bytes_with_nul())
176 }
177}
178
179/// An UCS-2 null-terminated string.
180///
181/// This type is largely inspired by [`core::ffi::CStr`] with the exception that all characters are
182/// guaranteed to be 16 bit long.
183///
184/// For convenience, a [`CStr16`] is comparable with [`core::str`] and
185/// `alloc::string::String` from the standard library through the trait [`EqStrUntilNul`].
186#[derive(Eq, PartialEq)]
187#[repr(transparent)]
188pub struct CStr16([Char16]);
189
190impl CStr16 {
191 /// Wraps a raw UEFI string with a safe C string wrapper
192 ///
193 /// # Safety
194 ///
195 /// The function will start accessing memory from `ptr` until the first
196 /// null byte. It's the callers responsibility to ensure `ptr` points to
197 /// a valid string, in accessible memory.
198 #[must_use]
199 pub unsafe fn from_ptr<'ptr>(ptr: *const Char16) -> &'ptr Self {
200 let mut len = 0;
201 while *ptr.add(len) != NUL_16 {
202 len += 1
203 }
204 let ptr = ptr.cast::<u16>();
205 Self::from_u16_with_nul_unchecked(slice::from_raw_parts(ptr, len + 1))
206 }
207
208 /// Creates a C string wrapper from a u16 slice
209 ///
210 /// Since not every u16 value is a valid UCS-2 code point, this function
211 /// must do a bit more validity checking than CStr::from_bytes_with_nul
212 pub fn from_u16_with_nul(codes: &[u16]) -> Result<&Self, FromSliceWithNulError> {
213 for (pos, &code) in codes.iter().enumerate() {
214 match code.try_into() {
215 Ok(NUL_16) => {
216 if pos != codes.len() - 1 {
217 return Err(FromSliceWithNulError::InteriorNul(pos));
218 } else {
219 return Ok(unsafe { Self::from_u16_with_nul_unchecked(codes) });
220 }
221 }
222 Err(_) => {
223 return Err(FromSliceWithNulError::InvalidChar(pos));
224 }
225 _ => {}
226 }
227 }
228 Err(FromSliceWithNulError::NotNulTerminated)
229 }
230
231 /// Unsafely creates a C string wrapper from a u16 slice.
232 ///
233 /// # Safety
234 ///
235 /// It's the callers responsibility to ensure chars is a valid UCS-2
236 /// null-terminated string, with no interior null bytes.
237 #[must_use]
238 pub const unsafe fn from_u16_with_nul_unchecked(codes: &[u16]) -> &Self {
239 &*(codes as *const [u16] as *const Self)
240 }
241
242 /// Convert a [`&str`] to a `&CStr16`, backed by a buffer.
243 ///
244 /// The input string must contain only characters representable with
245 /// UCS-2, and must not contain any null characters (even at the end of
246 /// the input).
247 ///
248 /// The backing buffer must be big enough to hold the converted string as
249 /// well as a trailing null character.
250 ///
251 /// # Examples
252 ///
253 /// Convert the UTF-8 string "ABC" to a `&CStr16`:
254 ///
255 /// ```
256 /// use uefi::CStr16;
257 ///
258 /// let mut buf = [0; 4];
259 /// CStr16::from_str_with_buf("ABC", &mut buf).unwrap();
260 /// ```
261 pub fn from_str_with_buf<'a>(
262 input: &str,
263 buf: &'a mut [u16],
264 ) -> Result<&'a Self, FromStrWithBufError> {
265 let mut index = 0;
266
267 // Convert to UTF-16.
268 for c in input.encode_utf16() {
269 *buf.get_mut(index)
270 .ok_or(FromStrWithBufError::BufferTooSmall)? = c;
271 index += 1;
272 }
273
274 // Add trailing null character.
275 *buf.get_mut(index)
276 .ok_or(FromStrWithBufError::BufferTooSmall)? = 0;
277
278 // Convert from u16 to Char16. This checks for invalid UCS-2 chars and
279 // interior nulls. The NotNulTerminated case is unreachable because we
280 // just added a trailing null character.
281 Self::from_u16_with_nul(&buf[..index + 1]).map_err(|err| match err {
282 FromSliceWithNulError::InvalidChar(p) => FromStrWithBufError::InvalidChar(p),
283 FromSliceWithNulError::InteriorNul(p) => FromStrWithBufError::InteriorNul(p),
284 FromSliceWithNulError::NotNulTerminated => unreachable!(),
285 })
286 }
287
288 /// Create a [`CStr16`] from an [`UnalignedSlice`] using an aligned
289 /// buffer for storage. The lifetime of the output is tied to `buf`,
290 /// not `src`.
291 pub fn from_unaligned_slice<'buf>(
292 src: &UnalignedSlice<'_, u16>,
293 buf: &'buf mut [MaybeUninit<u16>],
294 ) -> Result<&'buf CStr16, UnalignedCStr16Error> {
295 // The input `buf` might be longer than needed, so get a
296 // subslice of the required length.
297 let buf = buf
298 .get_mut(..src.len())
299 .ok_or(UnalignedCStr16Error::BufferTooSmall)?;
300
301 src.copy_to_maybe_uninit(buf);
302 let buf = unsafe {
303 // Safety: `copy_buf` fully initializes the slice.
304 maybe_uninit_slice_assume_init_ref(buf)
305 };
306 CStr16::from_u16_with_nul(buf).map_err(|e| match e {
307 FromSliceWithNulError::InvalidChar(v) => UnalignedCStr16Error::InvalidChar(v),
308 FromSliceWithNulError::InteriorNul(v) => UnalignedCStr16Error::InteriorNul(v),
309 FromSliceWithNulError::NotNulTerminated => UnalignedCStr16Error::NotNulTerminated,
310 })
311 }
312
313 /// Returns the inner pointer to this C string
314 #[must_use]
315 pub const fn as_ptr(&self) -> *const Char16 {
316 self.0.as_ptr()
317 }
318
319 /// Get the underlying [`Char16`] slice, including the trailing null.
320 #[must_use]
321 pub const fn as_slice_with_nul(&self) -> &[Char16] {
322 &self.0
323 }
324
325 /// Converts this C string to a u16 slice
326 #[must_use]
327 pub fn to_u16_slice(&self) -> &[u16] {
328 let chars = self.to_u16_slice_with_nul();
329 &chars[..chars.len() - 1]
330 }
331
332 /// Converts this C string to a u16 slice containing the trailing 0 char
333 #[must_use]
334 pub const fn to_u16_slice_with_nul(&self) -> &[u16] {
335 unsafe { &*(&self.0 as *const [Char16] as *const [u16]) }
336 }
337
338 /// Returns an iterator over this C string
339 #[must_use]
340 pub const fn iter(&self) -> CStr16Iter {
341 CStr16Iter {
342 inner: self,
343 pos: 0,
344 }
345 }
346
347 /// Get the number of bytes in the string (including the trailing null character).
348 #[must_use]
349 pub const fn num_bytes(&self) -> usize {
350 self.0.len() * 2
351 }
352
353 /// Writes each [`Char16`] as a [`char`] (4 bytes long in Rust language) into the buffer.
354 /// It is up to the implementer of [`core::fmt::Write`] to convert the char to a string
355 /// with proper encoding/charset. For example, in the case of [`alloc::string::String`]
356 /// all Rust chars (UTF-32) get converted to UTF-8.
357 ///
358 /// ## Example
359 ///
360 /// ```ignore
361 /// let firmware_vendor_c16_str: CStr16 = ...;
362 /// // crate "arrayvec" uses stack-allocated arrays for Strings => no heap allocations
363 /// let mut buf = arrayvec::ArrayString::<128>::new();
364 /// firmware_vendor_c16_str.as_str_in_buf(&mut buf);
365 /// log::info!("as rust str: {}", buf.as_str());
366 /// ```
367 ///
368 /// [`alloc::string::String`]: https://doc.rust-lang.org/nightly/alloc/string/struct.String.html
369 pub fn as_str_in_buf(&self, buf: &mut dyn core::fmt::Write) -> core::fmt::Result {
370 for c16 in self.iter() {
371 buf.write_char(char::from(*c16))?;
372 }
373 Ok(())
374 }
375}
376
377impl<StrType: AsRef<str> + ?Sized> EqStrUntilNul<StrType> for CStr16 {
378 fn eq_str_until_nul(&self, other: &StrType) -> bool {
379 let other: &str = other.as_ref();
380
381 let any_not_equal: bool = self
382 .iter()
383 .copied()
384 .map(char::from)
385 .zip(other.chars())
386 // This only works as CStr16 is guaranteed to have a fixed character length
387 // (unlike UTF-8 or UTF-16).
388 .take_while(|(l: &char, r: &char)| *l != '\0' && *r != '\0')
389 .any(|(l: char, r: char)| l != r);
390
391 !any_not_equal
392 }
393}
394
395/// An iterator over `CStr16`.
396#[derive(Debug)]
397pub struct CStr16Iter<'a> {
398 inner: &'a CStr16,
399 pos: usize,
400}
401
402impl<'a> Iterator for CStr16Iter<'a> {
403 type Item = &'a Char16;
404
405 fn next(&mut self) -> Option<Self::Item> {
406 if self.pos >= self.inner.0.len() - 1 {
407 None
408 } else {
409 self.pos += 1;
410 self.inner.0.get(self.pos - 1)
411 }
412 }
413}
414
415impl fmt::Debug for CStr16 {
416 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
417 write!(f, "CStr16({:?})", &self.0)
418 }
419}
420
421impl fmt::Display for CStr16 {
422 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
423 for c: &Char16 in self.iter() {
424 <Char16 as fmt::Display>::fmt(self:c, f)?;
425 }
426 Ok(())
427 }
428}
429
430#[cfg(feature = "alloc")]
431impl PartialEq<CString16> for &CStr16 {
432 fn eq(&self, other: &CString16) -> bool {
433 PartialEq::eq(*self, other.as_ref())
434 }
435}
436
437impl<'a> UnalignedSlice<'a, u16> {
438 /// Create a [`CStr16`] from an [`UnalignedSlice`] using an aligned
439 /// buffer for storage. The lifetime of the output is tied to `buf`,
440 /// not `self`.
441 pub fn to_cstr16<'buf>(
442 &self,
443 buf: &'buf mut [MaybeUninit<u16>],
444 ) -> Result<&'buf CStr16, UnalignedCStr16Error> {
445 CStr16::from_unaligned_slice(self, buf)
446 }
447}
448
449/// The EqStrUntilNul trait helps to compare Rust strings against UEFI string types (UCS-2 strings).
450/// The given generic implementation of this trait enables us that we only have to
451/// implement one direction (`left.eq_str_until_nul(&right)`) for each UEFI string type and we
452/// get the other direction (`right.eq_str_until_nul(&left)`) for free. Hence, the relation is
453/// reflexive.
454pub trait EqStrUntilNul<StrType: ?Sized> {
455 /// Checks if the provided Rust string `StrType` is equal to [Self] until the first null-byte
456 /// is found. An exception is the terminating null-byte of [Self] which is ignored.
457 ///
458 /// As soon as the first null byte in either `&self` or `other` is found, this method returns.
459 /// Note that Rust strings are allowed to contain null-bytes that do not terminate the string.
460 /// Although this is rather unusual, you can compare `"foo\0bar"` with an instance of [Self].
461 /// In that case, only `foo"` is compared against [Self] (if [Self] is long enough).
462 fn eq_str_until_nul(&self, other: &StrType) -> bool;
463}
464
465// magic implementation which transforms an existing `left.eq_str_until_nul(&right)` implementation
466// into an additional working `right.eq_str_until_nul(&left)` implementation.
467impl<StrType, C16StrType> EqStrUntilNul<C16StrType> for StrType
468where
469 StrType: AsRef<str>,
470 C16StrType: EqStrUntilNul<StrType> + ?Sized,
471{
472 fn eq_str_until_nul(&self, other: &C16StrType) -> bool {
473 // reuse the existing implementation
474 other.eq_str_until_nul(self)
475 }
476}
477
478#[cfg(test)]
479mod tests {
480 use super::*;
481 use alloc::string::String;
482 use uefi_macros::{cstr16, cstr8};
483
484 // Tests if our CStr8 type can be constructed from a valid core::ffi::CStr
485 #[test]
486 fn test_cstr8_from_cstr() {
487 let msg = "hello world\0";
488 let cstr = unsafe { CStr::from_ptr(msg.as_ptr().cast()) };
489 let cstr8: &CStr8 = TryFrom::try_from(cstr).unwrap();
490 assert!(cstr8.eq_str_until_nul(msg));
491 assert!(msg.eq_str_until_nul(cstr8));
492 }
493
494 #[test]
495 fn test_cstr16_num_bytes() {
496 let s = CStr16::from_u16_with_nul(&[65, 66, 67, 0]).unwrap();
497 assert_eq!(s.num_bytes(), 8);
498 }
499
500 #[test]
501 fn test_cstr16_from_str_with_buf() {
502 let mut buf = [0; 4];
503
504 // OK: buf is exactly the right size.
505 let s = CStr16::from_str_with_buf("ABC", &mut buf).unwrap();
506 assert_eq!(s.to_u16_slice_with_nul(), [65, 66, 67, 0]);
507
508 // OK: buf is bigger than needed.
509 let s = CStr16::from_str_with_buf("A", &mut buf).unwrap();
510 assert_eq!(s.to_u16_slice_with_nul(), [65, 0]);
511
512 // Error: buf is too small.
513 assert_eq!(
514 CStr16::from_str_with_buf("ABCD", &mut buf).unwrap_err(),
515 FromStrWithBufError::BufferTooSmall
516 );
517
518 // Error: invalid character.
519 assert_eq!(
520 CStr16::from_str_with_buf("a😀", &mut buf).unwrap_err(),
521 FromStrWithBufError::InvalidChar(1),
522 );
523
524 // Error: interior null.
525 assert_eq!(
526 CStr16::from_str_with_buf("a\0b", &mut buf).unwrap_err(),
527 FromStrWithBufError::InteriorNul(1),
528 );
529 }
530
531 #[test]
532 fn test_cstr16_macro() {
533 // Just a sanity check to make sure it's spitting out the right characters
534 assert_eq!(
535 crate::prelude::cstr16!("ABC").to_u16_slice_with_nul(),
536 [65, 66, 67, 0]
537 )
538 }
539
540 #[test]
541 fn test_unaligned_cstr16() {
542 let mut buf = [0u16; 6];
543 let us = unsafe {
544 let ptr = buf.as_mut_ptr() as *mut u8;
545 // Intentionally create an unaligned u16 pointer. This
546 // leaves room for five u16 characters.
547 let ptr = ptr.add(1) as *mut u16;
548 // Write out the "test" string.
549 ptr.add(0).write_unaligned(b't'.into());
550 ptr.add(1).write_unaligned(b'e'.into());
551 ptr.add(2).write_unaligned(b's'.into());
552 ptr.add(3).write_unaligned(b't'.into());
553 ptr.add(4).write_unaligned(b'\0'.into());
554
555 // Create the `UnalignedSlice`.
556 UnalignedSlice::new(ptr, 5)
557 };
558
559 // Test `to_cstr16()` with too small of a buffer.
560 let mut buf = [MaybeUninit::new(0); 4];
561 assert_eq!(
562 us.to_cstr16(&mut buf).unwrap_err(),
563 UnalignedCStr16Error::BufferTooSmall
564 );
565 // Test with a big enough buffer.
566 let mut buf = [MaybeUninit::new(0); 5];
567 assert_eq!(
568 us.to_cstr16(&mut buf).unwrap(),
569 CString16::try_from("test").unwrap()
570 );
571
572 // Test `to_cstring16()`.
573 assert_eq!(
574 us.to_cstring16().unwrap(),
575 CString16::try_from("test").unwrap()
576 );
577 }
578
579 // Code generation helper for the compare tests of our CStrX types against "str" and "String"
580 // from the standard library.
581 #[allow(non_snake_case)]
582 macro_rules! test_compare_cstrX {
583 ($input:ident) => {
584 assert!($input.eq_str_until_nul(&"test"));
585 assert!($input.eq_str_until_nul(&String::from("test")));
586
587 // now other direction
588 assert!(String::from("test").eq_str_until_nul($input));
589 assert!("test".eq_str_until_nul($input));
590
591 // some more tests
592 // this is fine: compare until the first null
593 assert!($input.eq_str_until_nul(&"te\0st"));
594 // this is fine
595 assert!($input.eq_str_until_nul(&"test\0"));
596 assert!(!$input.eq_str_until_nul(&"hello"));
597 };
598 }
599
600 #[test]
601 fn test_compare_cstr8() {
602 // test various comparisons with different order (left, right)
603 let input: &CStr8 = cstr8!("test");
604 test_compare_cstrX!(input);
605 }
606
607 #[test]
608 fn test_compare_cstr16() {
609 let input: &CStr16 = cstr16!("test");
610 test_compare_cstrX!(input);
611 }
612
613 /// Test that the `cstr16!` macro can be used in a `const` context.
614 #[test]
615 fn test_cstr16_macro_const() {
616 const S: &CStr16 = cstr16!("ABC");
617 assert_eq!(S.to_u16_slice_with_nul(), [65, 66, 67, 0]);
618 }
619
620 /// Tests the trait implementation of trait [`EqStrUntilNul]` for [`CStr8`].
621 ///
622 /// This tests that `String` and `str` from the standard library can be
623 /// checked for equality against a [`CStr8`]. It checks both directions,
624 /// i.e., the equality is reflexive.
625 #[test]
626 fn test_cstr8_eq_std_str() {
627 let input: &CStr8 = cstr8!("test");
628
629 // test various comparisons with different order (left, right)
630 assert!(input.eq_str_until_nul("test")); // requires ?Sized constraint
631 assert!(input.eq_str_until_nul(&"test"));
632 assert!(input.eq_str_until_nul(&String::from("test")));
633
634 // now other direction
635 assert!(String::from("test").eq_str_until_nul(input));
636 assert!("test".eq_str_until_nul(input));
637 }
638
639 /// Tests the trait implementation of trait [`EqStrUntilNul]` for [`CStr16`].
640 ///
641 /// This tests that `String` and `str` from the standard library can be
642 /// checked for equality against a [`CStr16`]. It checks both directions,
643 /// i.e., the equality is reflexive.
644 #[test]
645 fn test_cstr16_eq_std_str() {
646 let input: &CStr16 = cstr16!("test");
647
648 assert!(input.eq_str_until_nul("test")); // requires ?Sized constraint
649 assert!(input.eq_str_until_nul(&"test"));
650 assert!(input.eq_str_until_nul(&String::from("test")));
651
652 // now other direction
653 assert!(String::from("test").eq_str_until_nul(input));
654 assert!("test".eq_str_until_nul(input));
655 }
656}
657