@@ -120,6 +120,9 @@ impl CStr8 {
120
120
///
121
121
/// This type is largely inspired by `std::ffi::CStr`, see the documentation of
122
122
/// `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].
123
126
#[ derive( Eq , PartialEq ) ]
124
127
#[ repr( transparent) ]
125
128
pub struct CStr16 ( [ Char16 ] ) ;
@@ -278,6 +281,22 @@ impl CStr16 {
278
281
}
279
282
}
280
283
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
+
281
300
/// An iterator over `CStr16`.
282
301
#[ derive( Debug ) ]
283
302
pub struct CStr16Iter < ' a > {
@@ -427,9 +446,39 @@ impl<'a> UnalignedCStr16<'a> {
427
446
}
428
447
}
429
448
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
+
430
477
#[ cfg( test) ]
431
478
mod tests {
432
479
use super :: * ;
480
+ use crate :: alloc_api:: string:: String ;
481
+ use uefi_macros:: cstr16;
433
482
434
483
#[ test]
435
484
fn test_cstr16_num_bytes ( ) {
@@ -515,4 +564,24 @@ mod tests {
515
564
CString16 :: try_from( "test" ) . unwrap( )
516
565
) ;
517
566
}
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
+ }
518
587
}
0 commit comments