@@ -10,7 +10,31 @@ use core::slice;
10
10
#[ cfg( feature = "alloc" ) ]
11
11
use super :: CString16 ;
12
12
13
- /// Errors which can occur during checked `[uN]` -> `CStrN` conversions
13
+ /// Error converting from a slice (which can contain interior nuls) to a string
14
+ /// type.
15
+ #[ derive( Clone , Copy , Debug , Eq , PartialEq ) ]
16
+ pub enum FromSliceUntilNulError {
17
+ /// An invalid character was encountered before the end of the slice.
18
+ InvalidChar ( usize ) ,
19
+
20
+ /// The does not contain a nul character.
21
+ NoNul ,
22
+ }
23
+
24
+ impl Display for FromSliceUntilNulError {
25
+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> fmt:: Result {
26
+ match self {
27
+ Self :: InvalidChar ( usize) => write ! ( f, "invalid character at index {}" , usize ) ,
28
+ Self :: NoNul => write ! ( f, "no nul character" ) ,
29
+ }
30
+ }
31
+ }
32
+
33
+ #[ cfg( feature = "unstable" ) ]
34
+ impl core:: error:: Error for FromSliceUntilNulError { }
35
+
36
+ /// Error converting from a slice (which cannot contain interior nuls) to a
37
+ /// string type.
14
38
#[ derive( Clone , Copy , Debug , Eq , PartialEq ) ]
15
39
pub enum FromSliceWithNulError {
16
40
/// An invalid character was encountered before the end of the slice
@@ -337,6 +361,23 @@ impl CStr16 {
337
361
Self :: from_u16_with_nul_unchecked ( slice:: from_raw_parts ( ptr, len + 1 ) )
338
362
}
339
363
364
+ /// Creates a `&CStr16` from a u16 slice, stopping at the first nul character.
365
+ ///
366
+ /// # Errors
367
+ ///
368
+ /// An error is returned if the slice contains invalid UCS-2 characters, or
369
+ /// if the slice does not contain any nul character.
370
+ pub fn from_u16_until_nul ( codes : & [ u16 ] ) -> Result < & Self , FromSliceUntilNulError > {
371
+ for ( pos, & code) in codes. iter ( ) . enumerate ( ) {
372
+ let chr =
373
+ Char16 :: try_from ( code) . map_err ( |_| FromSliceUntilNulError :: InvalidChar ( pos) ) ?;
374
+ if chr == NUL_16 {
375
+ return Ok ( unsafe { Self :: from_u16_with_nul_unchecked ( & codes[ ..=pos] ) } ) ;
376
+ }
377
+ }
378
+ Err ( FromSliceUntilNulError :: NoNul )
379
+ }
380
+
340
381
/// Creates a `&CStr16` from a u16 slice, if the slice contains exactly
341
382
/// one terminating null-byte and all chars are valid UCS-2 chars.
342
383
pub fn from_u16_with_nul ( codes : & [ u16 ] ) -> Result < & Self , FromSliceWithNulError > {
@@ -369,6 +410,22 @@ impl CStr16 {
369
410
& * ( codes as * const [ u16 ] as * const Self )
370
411
}
371
412
413
+ /// Creates a `&CStr16` from a [`Char16`] slice, stopping at the first nul character.
414
+ ///
415
+ /// # Errors
416
+ ///
417
+ /// An error is returned if the slice does not contain any nul character.
418
+ pub fn from_char16_until_nul ( chars : & [ Char16 ] ) -> Result < & Self , FromSliceUntilNulError > {
419
+ // Find the index of the first null char.
420
+ let end = chars
421
+ . iter ( )
422
+ . position ( |c| * c == NUL_16 )
423
+ . ok_or ( FromSliceUntilNulError :: NoNul ) ?;
424
+
425
+ // Safety: the input is nul-terminated.
426
+ unsafe { Ok ( Self :: from_char16_with_nul_unchecked ( & chars[ ..=end] ) ) }
427
+ }
428
+
372
429
/// Creates a `&CStr16` from a [`Char16`] slice, if the slice is
373
430
/// null-terminated and has no interior null characters.
374
431
pub fn from_char16_with_nul ( chars : & [ Char16 ] ) -> Result < & Self , FromSliceWithNulError > {
@@ -734,6 +791,75 @@ mod tests {
734
791
assert_eq ! ( s. num_bytes( ) , 8 ) ;
735
792
}
736
793
794
+ #[ test]
795
+ fn test_cstr16_from_u16_until_nul ( ) {
796
+ // Invalid: empty input.
797
+ assert_eq ! (
798
+ CStr16 :: from_u16_until_nul( & [ ] ) ,
799
+ Err ( FromSliceUntilNulError :: NoNul )
800
+ ) ;
801
+
802
+ // Invalid: no nul.
803
+ assert_eq ! (
804
+ CStr16 :: from_u16_until_nul( & [ 65 , 66 ] ) ,
805
+ Err ( FromSliceUntilNulError :: NoNul )
806
+ ) ;
807
+
808
+ // Invalid: not UCS-2.
809
+ assert_eq ! (
810
+ CStr16 :: from_u16_until_nul( & [ 65 , 0xde01 , 0 ] ) ,
811
+ Err ( FromSliceUntilNulError :: InvalidChar ( 1 ) )
812
+ ) ;
813
+
814
+ // Valid: trailing nul.
815
+ assert_eq ! ( CStr16 :: from_u16_until_nul( & [ 97 , 98 , 0 , ] ) , Ok ( cstr16!( "ab" ) ) ) ;
816
+
817
+ // Valid: interior nul.
818
+ assert_eq ! (
819
+ CStr16 :: from_u16_until_nul( & [ 97 , 0 , 98 , 0 , ] ) ,
820
+ Ok ( cstr16!( "a" ) )
821
+ ) ;
822
+ }
823
+
824
+ #[ test]
825
+ fn test_cstr16_from_char16_until_nul ( ) {
826
+ // Invalid: empty input.
827
+ assert_eq ! (
828
+ CStr16 :: from_char16_until_nul( & [ ] ) ,
829
+ Err ( FromSliceUntilNulError :: NoNul )
830
+ ) ;
831
+
832
+ // Invalid: no nul character.
833
+ assert_eq ! (
834
+ CStr16 :: from_char16_until_nul( & [
835
+ Char16 :: try_from( 'a' ) . unwrap( ) ,
836
+ Char16 :: try_from( 'b' ) . unwrap( ) ,
837
+ ] ) ,
838
+ Err ( FromSliceUntilNulError :: NoNul )
839
+ ) ;
840
+
841
+ // Valid: trailing nul.
842
+ assert_eq ! (
843
+ CStr16 :: from_char16_until_nul( & [
844
+ Char16 :: try_from( 'a' ) . unwrap( ) ,
845
+ Char16 :: try_from( 'b' ) . unwrap( ) ,
846
+ NUL_16 ,
847
+ ] ) ,
848
+ Ok ( cstr16!( "ab" ) )
849
+ ) ;
850
+
851
+ // Valid: interior nul.
852
+ assert_eq ! (
853
+ CStr16 :: from_char16_until_nul( & [
854
+ Char16 :: try_from( 'a' ) . unwrap( ) ,
855
+ NUL_16 ,
856
+ Char16 :: try_from( 'b' ) . unwrap( ) ,
857
+ NUL_16
858
+ ] ) ,
859
+ Ok ( cstr16!( "a" ) )
860
+ ) ;
861
+ }
862
+
737
863
#[ test]
738
864
fn test_cstr16_from_char16_with_nul ( ) {
739
865
// Invalid: empty input.
0 commit comments