@@ -120,6 +120,9 @@ impl CStr8 {
120120///
121121/// This type is largely inspired by `std::ffi::CStr`, see the documentation of
122122/// `CStr` for more details on its semantics.
123+ ///
124+ /// For convenience, a [CStr16] is comparable with `&str` and `String` from the standard library
125+ /// through the trait [EqStrUntilNul].
123126#[ derive( Eq , PartialEq ) ]
124127#[ repr( transparent) ]
125128pub struct CStr16 ( [ Char16 ] ) ;
@@ -278,6 +281,22 @@ impl CStr16 {
278281 }
279282}
280283
284+ impl < StrType : AsRef < str > > EqStrUntilNul < StrType > for CStr16 {
285+ fn eq_str_until_nul ( & self , other : & StrType ) -> bool {
286+ let other = other. as_ref ( ) ;
287+
288+ let any_not_equal = self
289+ . iter ( )
290+ . copied ( )
291+ . map ( char:: from)
292+ . zip ( other. chars ( ) )
293+ . take_while ( |( l, r) | * l != '\0' && * r != '\0' )
294+ . any ( |( l, r) | l != r) ;
295+
296+ !any_not_equal
297+ }
298+ }
299+
281300/// An iterator over `CStr16`.
282301#[ derive( Debug ) ]
283302pub struct CStr16Iter < ' a > {
@@ -427,9 +446,39 @@ impl<'a> UnalignedCStr16<'a> {
427446 }
428447}
429448
449+ /// Trait that helps to compare Rust strings against CStr16 types.
450+ /// A generic implementation of this trait enables us that we only have to
451+ /// implement one direction (`left.eq_str_until_nul(&right)`) and we get
452+ /// the other direction (`right.eq_str_until_nul(&left)`) for free.
453+ pub trait EqStrUntilNul < StrType : ?Sized > {
454+ /// Checks if the provided Rust string `StrType` is equal to [Self] until the first null-byte
455+ /// is found. An exception is the terminating null-byte of [Self] which is ignored.
456+ ///
457+ /// As soon as the first null byte in either `&self` or `other` is found, this method returns.
458+ /// Note that Rust strings are allowed to contain null-bytes that do not terminate the string.
459+ /// Although this is rather unusual, you can compare `"foo\0bar"` with an instance of [Self].
460+ /// In that case, only `foo"` is compared against [Self] (if [Self] is long enough).
461+ fn eq_str_until_nul ( & self , other : & StrType ) -> bool ;
462+ }
463+
464+ // magic implementation which transforms an existing `left.eq_str_until_nul(&right)` implementation
465+ // into an additional working `right.eq_str_until_nul(&left)` implementation.
466+ impl < StrType , C16StrType > EqStrUntilNul < C16StrType > for StrType
467+ where
468+ StrType : AsRef < str > ,
469+ C16StrType : EqStrUntilNul < StrType > + ?Sized ,
470+ {
471+ fn eq_str_until_nul ( & self , other : & C16StrType ) -> bool {
472+ // reuse the existing implementation
473+ other. eq_str_until_nul ( self )
474+ }
475+ }
476+
430477#[ cfg( test) ]
431478mod tests {
432479 use super :: * ;
480+ use crate :: alloc_api:: string:: String ;
481+ use uefi_macros:: cstr16;
433482
434483 #[ test]
435484 fn test_cstr16_num_bytes ( ) {
@@ -515,4 +564,24 @@ mod tests {
515564 CString16 :: try_from( "test" ) . unwrap( )
516565 ) ;
517566 }
567+
568+ #[ test]
569+ 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" ) ) ;
586+ }
518587}
0 commit comments