11use super :: chars:: { Char16 , Char8 , NUL_16 , NUL_8 } ;
2+ use core:: ffi:: CStr ;
23use core:: fmt;
34use core:: iter:: Iterator ;
45use core:: marker:: PhantomData ;
@@ -52,21 +53,30 @@ pub enum FromStrWithBufError {
5253 BufferTooSmall ,
5354}
5455
55- /// A Latin-1 null-terminated string
56+ /// A Latin-1 null-terminated string.
5657///
57- /// This type is largely inspired by `std::ffi::CStr`, see the documentation of
58- /// `CStr` for more details on its semantics.
58+ /// This type is largely inspired by [core::ffi::CStr] with the exception that all characters are
59+ /// guaranteed to be 8 bit long. A [CStr8] can be constructed from a [core::ffi::CStr] via a
60+ /// `try_from` call:
61+ ///
62+ /// ```ignore
63+ /// let cstr8: &CStr8 = TryFrom::try_from(cstr).unwrap();
64+ /// ```
65+ ///
66+ /// For convenience, a [CStr8] is comparable with `&str` and `String` from the standard library
67+ /// through the trait [EqStrUntilNul].
5968#[ repr( transparent) ]
69+ #[ derive( Eq , PartialEq ) ]
6070pub struct CStr8 ( [ Char8 ] ) ;
6171
6272impl CStr8 {
63- /// Wraps a raw UEFI string with a safe C string wrapper
73+ /// Wraps a raw null-terminated 8 bit ASCII (Latin 1) with a CStr8 reference.
6474 ///
6575 /// # Safety
6676 ///
6777 /// The function will start accessing memory from `ptr` until the first
68- /// null byte. It's the callers responsability to ensure `ptr` points to
69- /// a valid string, in accessible memory.
78+ /// null byte. It's the callers responsibility to ensure `ptr` points to
79+ /// a valid null-terminated string in accessible memory.
7080 pub unsafe fn from_ptr < ' ptr > ( ptr : * const Char8 ) -> & ' ptr Self {
7181 let mut len = 0 ;
7282 while * ptr. add ( len) != NUL_8 {
@@ -76,7 +86,7 @@ impl CStr8 {
7686 Self :: from_bytes_with_nul_unchecked ( slice:: from_raw_parts ( ptr, len + 1 ) )
7787 }
7888
79- /// Creates a C string wrapper from bytes
89+ /// Creates a CStr8 reference from bytes.
8090 pub fn from_bytes_with_nul ( chars : & [ u8 ] ) -> Result < & Self , FromSliceWithNulError > {
8191 let nul_pos = chars. iter ( ) . position ( |& c| c == 0 ) ;
8292 if let Some ( nul_pos) = nul_pos {
@@ -89,7 +99,7 @@ impl CStr8 {
8999 }
90100 }
91101
92- /// Unsafely creates a C string wrapper from bytes
102+ /// Unsafely creates a CStr8 reference from bytes.
93103 ///
94104 /// # Safety
95105 ///
@@ -99,23 +109,65 @@ impl CStr8 {
99109 & * ( chars as * const [ u8 ] as * const Self )
100110 }
101111
102- /// Returns the inner pointer to this C string
112+ /// Returns the inner pointer to this CStr8.
103113 pub fn as_ptr ( & self ) -> * const Char8 {
104114 self . 0 . as_ptr ( )
105115 }
106116
107- /// Converts this C string to a slice of bytes
117+ /// Converts this CStr8 to a slice of bytes without the terminating null byte.
108118 pub fn to_bytes ( & self ) -> & [ u8 ] {
109119 let chars = self . to_bytes_with_nul ( ) ;
110120 & chars[ ..chars. len ( ) - 1 ]
111121 }
112122
113- /// Converts this C string to a slice of bytes containing the trailing 0 char
123+ /// Converts this CStr8 to a slice of bytes containing the trailing null byte.
114124 pub fn to_bytes_with_nul ( & self ) -> & [ u8 ] {
115125 unsafe { & * ( & self . 0 as * const [ Char8 ] as * const [ u8 ] ) }
116126 }
117127}
118128
129+ impl fmt:: Debug for CStr8 {
130+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
131+ write ! ( f, "CStr8({:?})" , & self . 0 )
132+ }
133+ }
134+
135+ impl fmt:: Display for CStr8 {
136+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
137+ for c in self . 0 . iter ( ) {
138+ <Char8 as fmt:: Display >:: fmt ( c, f) ?;
139+ }
140+ Ok ( ( ) )
141+ }
142+ }
143+
144+ impl < StrType : AsRef < str > > EqStrUntilNul < StrType > for CStr8 {
145+ fn eq_str_until_nul ( & self , other : & StrType ) -> bool {
146+ let other = other. as_ref ( ) ;
147+
148+ // TODO: CStr16 has .iter() implemented, CStr8 not yet
149+ let any_not_equal = self
150+ . 0
151+ . iter ( )
152+ . copied ( )
153+ . map ( char:: from)
154+ . zip ( other. chars ( ) )
155+ // this only works as CStr8 is guaranteed to have a fixed character length
156+ . take_while ( |( l, r) | * l != '\0' && * r != '\0' )
157+ . any ( |( l, r) | l != r) ;
158+
159+ !any_not_equal
160+ }
161+ }
162+
163+ impl < ' a > TryFrom < & ' a CStr > for & ' a CStr8 {
164+ type Error = FromSliceWithNulError ;
165+
166+ fn try_from ( cstr : & ' a CStr ) -> Result < Self , Self :: Error > {
167+ CStr8 :: from_bytes_with_nul ( cstr. to_bytes_with_nul ( ) )
168+ }
169+ }
170+
119171/// An UCS-2 null-terminated string
120172///
121173/// This type is largely inspired by `std::ffi::CStr`, see the documentation of
@@ -290,6 +342,7 @@ impl<StrType: AsRef<str>> EqStrUntilNul<StrType> for CStr16 {
290342 . copied ( )
291343 . map ( char:: from)
292344 . zip ( other. chars ( ) )
345+ // this only works as CStr16 is guaranteed to have a fixed character length
293346 . take_while ( |( l, r) | * l != '\0' && * r != '\0' )
294347 . any ( |( l, r) | l != r) ;
295348
@@ -478,7 +531,17 @@ where
478531mod tests {
479532 use super :: * ;
480533 use crate :: alloc_api:: string:: String ;
481- use uefi_macros:: cstr16;
534+ use uefi_macros:: { cstr16, cstr8} ;
535+
536+ // Tests if our CStr8 type can be constructed from a valid core::ffi::CStr
537+ #[ test]
538+ fn test_cstr8_from_cstr ( ) {
539+ let msg = "hello world\0 " ;
540+ let cstr = unsafe { CStr :: from_ptr ( msg. as_ptr ( ) . cast ( ) ) } ;
541+ let cstr8: & CStr8 = TryFrom :: try_from ( cstr) . unwrap ( ) ;
542+ assert ! ( cstr8. eq_str_until_nul( & msg) ) ;
543+ assert ! ( msg. eq_str_until_nul( cstr8) ) ;
544+ }
482545
483546 #[ test]
484547 fn test_cstr16_num_bytes ( ) {
@@ -567,21 +630,41 @@ mod tests {
567630
568631 #[ test]
569632 fn test_compare ( ) {
570- let input: & CStr16 = cstr16 ! ( "test" ) ;
571-
572- // test various comparisons with different order (left, right)
573- assert ! ( input. eq_str_until_nul( & "test" ) ) ;
574- assert ! ( input. eq_str_until_nul( & String :: from( "test" ) ) ) ;
575-
576- // now other direction
577- assert ! ( String :: from( "test" ) . eq_str_until_nul( input) ) ;
578- assert ! ( "test" . eq_str_until_nul( input) ) ;
579-
580- // some more tests
581- // this is fine: compare until the first null
582- assert ! ( input. eq_str_until_nul( & "te\0 st" ) ) ;
583- // this is fine
584- assert ! ( input. eq_str_until_nul( & "test\0 " ) ) ;
585- assert ! ( !input. eq_str_until_nul( & "hello" ) ) ;
633+ // twice the same test: once for CStr8, once for CStr16
634+ {
635+ // test various comparisons with different order (left, right)
636+ let input: & CStr8 = cstr8 ! ( "test" ) ;
637+ assert ! ( input. eq_str_until_nul( & "test" ) ) ;
638+ assert ! ( input. eq_str_until_nul( & String :: from( "test" ) ) ) ;
639+
640+ // now other direction
641+ assert ! ( String :: from( "test" ) . eq_str_until_nul( input) ) ;
642+ assert ! ( "test" . eq_str_until_nul( input) ) ;
643+
644+ // some more tests
645+ // this is fine: compare until the first null
646+ assert ! ( input. eq_str_until_nul( & "te\0 st" ) ) ;
647+ // this is fine
648+ assert ! ( input. eq_str_until_nul( & "test\0 " ) ) ;
649+ assert ! ( !input. eq_str_until_nul( & "hello" ) ) ;
650+ }
651+ {
652+ let input: & CStr16 = cstr16 ! ( "test" ) ;
653+
654+ // test various comparisons with different order (left, right)
655+ assert ! ( input. eq_str_until_nul( & "test" ) ) ;
656+ assert ! ( input. eq_str_until_nul( & String :: from( "test" ) ) ) ;
657+
658+ // now other direction
659+ assert ! ( String :: from( "test" ) . eq_str_until_nul( input) ) ;
660+ assert ! ( "test" . eq_str_until_nul( input) ) ;
661+
662+ // some more tests
663+ // this is fine: compare until the first null
664+ assert ! ( input. eq_str_until_nul( & "te\0 st" ) ) ;
665+ // this is fine
666+ assert ! ( input. eq_str_until_nul( & "test\0 " ) ) ;
667+ assert ! ( !input. eq_str_until_nul( & "hello" ) ) ;
668+ }
586669 }
587670}
0 commit comments