1use 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#[derive(Clone, Copy, Debug, Eq, PartialEq)]
23pub enum FromSliceUntilNulError {
24 InvalidChar(usize),
26
27 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#[derive(Clone, Copy, Debug, Eq, PartialEq)]
45pub enum FromSliceWithNulError {
46 InvalidChar(usize),
48
49 InteriorNul(usize),
51
52 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#[derive(Clone, Copy, Debug, Eq, PartialEq)]
70pub enum UnalignedCStr16Error {
71 InvalidChar(usize),
73
74 InteriorNul(usize),
76
77 NotNulTerminated,
79
80 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#[derive(Clone, Copy, Debug, Eq, PartialEq)]
100pub enum FromStrWithBufError {
101 InvalidChar(usize),
103
104 InteriorNul(usize),
106
107 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#[repr(transparent)]
137#[derive(Eq, PartialEq, Ord, PartialOrd, Hash)]
138pub struct CStr8([Char8]);
139
140impl CStr8 {
141 #[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 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 #[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 #[must_use]
184 pub const fn as_ptr(&self) -> *const Char8 {
185 self.0.as_ptr()
186 }
187
188 #[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 let any_not_equal = self
229 .0
230 .iter()
231 .copied()
232 .map(char::from)
233 .zip(other.chars())
234 .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
251const 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 panic!("input string cannot be encoded as Latin-1");
277 }
278}
279
280#[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 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#[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 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 if output_offset + 1 != N {
332 panic!("incorrect array length");
333 }
334
335 output
336}
337
338#[derive(Eq, PartialEq, Ord, PartialOrd, Hash)]
346#[repr(transparent)]
347pub struct CStr16([Char16]);
348
349impl CStr16 {
350 #[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 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 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 #[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 pub fn from_char16_until_nul(chars: &[Char16]) -> Result<&Self, FromSliceUntilNulError> {
422 let end = chars
424 .iter()
425 .position(|c| *c == NUL_16)
426 .ok_or(FromSliceUntilNulError::NoNul)?;
427
428 unsafe { Ok(Self::from_char16_with_nul_unchecked(&chars[..=end])) }
430 }
431
432 pub fn from_char16_with_nul(chars: &[Char16]) -> Result<&Self, FromSliceWithNulError> {
435 if chars.is_empty() {
437 return Err(FromSliceWithNulError::NotNulTerminated);
438 }
439
440 if let Some(null_index) = chars.iter().position(|c| *c == NUL_16) {
442 if null_index == chars.len() - 1 {
444 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 #[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 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 for c in input.encode_utf16() {
493 *buf.get_mut(index)
494 .ok_or(FromStrWithBufError::BufferTooSmall)? = c;
495 index += 1;
496 }
497
498 *buf.get_mut(index)
500 .ok_or(FromStrWithBufError::BufferTooSmall)? = 0;
501
502 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 pub fn from_unaligned_slice<'buf>(
518 src: &UnalignedSlice<'_, u16>,
519 buf: &'buf mut [MaybeUninit<u16>],
520 ) -> Result<&'buf Self, UnalignedCStr16Error> {
521 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 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 #[must_use]
541 pub const fn as_ptr(&self) -> *const Char16 {
542 self.0.as_ptr()
543 }
544
545 #[must_use]
547 pub fn as_slice(&self) -> &[Char16] {
548 &self.0[..self.num_chars()]
549 }
550
551 #[must_use]
553 pub const fn as_slice_with_nul(&self) -> &[Char16] {
554 &self.0
555 }
556
557 #[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 #[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 #[must_use]
572 pub const fn iter(&self) -> CStr16Iter<'_> {
573 CStr16Iter {
574 inner: self,
575 pos: 0,
576 }
577 }
578
579 #[must_use]
581 pub const fn num_chars(&self) -> usize {
582 self.0.len() - 1
583 }
584
585 #[must_use]
587 pub const fn is_empty(&self) -> bool {
588 self.num_chars() == 0
589 }
590
591 #[must_use]
593 pub const fn num_bytes(&self) -> usize {
594 self.0.len() * 2
595 }
596
597 #[must_use]
599 pub fn is_ascii(&self) -> bool {
600 self.0.iter().all(|c| c.is_ascii())
601 }
602
603 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 #[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 .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#[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#[derive(Debug)]
733pub struct PoolString(PoolAllocation);
734
735impl PoolString {
736 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 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
772pub trait EqStrUntilNul<StrType: ?Sized> {
779 fn eq_str_until_nul(&self, other: &StrType) -> bool;
787}
788
789impl<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 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 #[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 assert_eq!(
849 CStr16::from_u16_until_nul(&[]),
850 Err(FromSliceUntilNulError::NoNul)
851 );
852
853 assert_eq!(
855 CStr16::from_u16_until_nul(&[65, 66]),
856 Err(FromSliceUntilNulError::NoNul)
857 );
858
859 assert_eq!(
861 CStr16::from_u16_until_nul(&[65, 0xde01, 0]),
862 Err(FromSliceUntilNulError::InvalidChar(1))
863 );
864
865 assert_eq!(CStr16::from_u16_until_nul(&[97, 98, 0,]), Ok(cstr16!("ab")));
867
868 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 assert_eq!(
879 CStr16::from_char16_until_nul(&[]),
880 Err(FromSliceUntilNulError::NoNul)
881 );
882
883 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 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 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 assert_eq!(
918 CStr16::from_char16_with_nul(&[]),
919 Err(FromSliceWithNulError::NotNulTerminated)
920 );
921
922 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 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 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 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 let s = CStr16::from_str_with_buf("A", &mut buf).unwrap();
963 assert_eq!(s.to_u16_slice_with_nul(), [65, 0]);
964
965 assert_eq!(
967 CStr16::from_str_with_buf("ABCD", &mut buf).unwrap_err(),
968 FromStrWithBufError::BufferTooSmall
969 );
970
971 assert_eq!(
973 CStr16::from_str_with_buf("a😀", &mut buf).unwrap_err(),
974 FromStrWithBufError::InvalidChar(1),
975 );
976
977 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 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 let ptr = ptr.add(1).cast::<u16>();
1001 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 UnalignedSlice::new(ptr, 5)
1010 };
1011
1012 let mut buf = [MaybeUninit::new(0); 4];
1014 assert_eq!(
1015 us.to_cstr16(&mut buf).unwrap_err(),
1016 UnalignedCStr16Error::BufferTooSmall
1017 );
1018 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 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 #[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 assert!(String::from("test").eq_str_until_nul($input));
1060 assert!("test".eq_str_until_nul($input));
1061
1062 assert!($input.eq_str_until_nul(&"te\0st"));
1065 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 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]
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 #[test]
1097 fn test_cstr8_eq_std_str() {
1098 let input: &CStr8 = cstr8!("test");
1099
1100 assert!(input.eq_str_until_nul("test")); assert!(input.eq_str_until_nul(&"test"));
1103 assert!(input.eq_str_until_nul(&String::from("test")));
1104
1105 assert!(String::from("test").eq_str_until_nul(input));
1107 assert!("test".eq_str_until_nul(input));
1108 }
1109
1110 #[test]
1116 fn test_cstr16_eq_std_str() {
1117 let input: &CStr16 = cstr16!("test");
1118
1119 assert!(input.eq_str_until_nul("test")); assert!(input.eq_str_until_nul(&"test"));
1121 assert!(input.eq_str_until_nul(&String::from("test")));
1122
1123 assert!(String::from("test").eq_str_until_nul(input));
1125 assert!("test".eq_str_until_nul(input));
1126 }
1127}