1
1
use super :: chars:: { Char16 , Char8 , NUL_16 , NUL_8 } ;
2
+ use core:: ffi:: CStr ;
2
3
use core:: fmt;
3
4
use core:: iter:: Iterator ;
4
5
use core:: marker:: PhantomData ;
@@ -52,21 +53,30 @@ pub enum FromStrWithBufError {
52
53
BufferTooSmall ,
53
54
}
54
55
55
- /// A Latin-1 null-terminated string
56
+ /// A Latin-1 null-terminated string.
56
57
///
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].
59
68
#[ repr( transparent) ]
69
+ #[ derive( Eq , PartialEq ) ]
60
70
pub struct CStr8 ( [ Char8 ] ) ;
61
71
62
72
impl 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.
64
74
///
65
75
/// # Safety
66
76
///
67
77
/// 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.
70
80
pub unsafe fn from_ptr < ' ptr > ( ptr : * const Char8 ) -> & ' ptr Self {
71
81
let mut len = 0 ;
72
82
while * ptr. add ( len) != NUL_8 {
@@ -76,7 +86,7 @@ impl CStr8 {
76
86
Self :: from_bytes_with_nul_unchecked ( slice:: from_raw_parts ( ptr, len + 1 ) )
77
87
}
78
88
79
- /// Creates a C string wrapper from bytes
89
+ /// Creates a CStr8 reference from bytes.
80
90
pub fn from_bytes_with_nul ( chars : & [ u8 ] ) -> Result < & Self , FromSliceWithNulError > {
81
91
let nul_pos = chars. iter ( ) . position ( |& c| c == 0 ) ;
82
92
if let Some ( nul_pos) = nul_pos {
@@ -89,7 +99,7 @@ impl CStr8 {
89
99
}
90
100
}
91
101
92
- /// Unsafely creates a C string wrapper from bytes
102
+ /// Unsafely creates a CStr8 reference from bytes.
93
103
///
94
104
/// # Safety
95
105
///
@@ -99,23 +109,65 @@ impl CStr8 {
99
109
& * ( chars as * const [ u8 ] as * const Self )
100
110
}
101
111
102
- /// Returns the inner pointer to this C string
112
+ /// Returns the inner pointer to this CStr8.
103
113
pub fn as_ptr ( & self ) -> * const Char8 {
104
114
self . 0 . as_ptr ( )
105
115
}
106
116
107
- /// Converts this C string to a slice of bytes
117
+ /// Converts this CStr8 to a slice of bytes without the terminating null byte.
108
118
pub fn to_bytes ( & self ) -> & [ u8 ] {
109
119
let chars = self . to_bytes_with_nul ( ) ;
110
120
& chars[ ..chars. len ( ) - 1 ]
111
121
}
112
122
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.
114
124
pub fn to_bytes_with_nul ( & self ) -> & [ u8 ] {
115
125
unsafe { & * ( & self . 0 as * const [ Char8 ] as * const [ u8 ] ) }
116
126
}
117
127
}
118
128
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
+
119
171
/// An UCS-2 null-terminated string
120
172
///
121
173
/// 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 {
290
342
. copied ( )
291
343
. map ( char:: from)
292
344
. zip ( other. chars ( ) )
345
+ // this only works as CStr16 is guaranteed to have a fixed character length
293
346
. take_while ( |( l, r) | * l != '\0' && * r != '\0' )
294
347
. any ( |( l, r) | l != r) ;
295
348
@@ -478,7 +531,17 @@ where
478
531
mod tests {
479
532
use super :: * ;
480
533
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
+ }
482
545
483
546
#[ test]
484
547
fn test_cstr16_num_bytes ( ) {
@@ -567,21 +630,41 @@ mod tests {
567
630
568
631
#[ test]
569
632
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
+ }
586
669
}
587
670
}
0 commit comments