@@ -10,7 +10,31 @@ use core::slice;
1010#[ cfg( feature = "alloc" ) ]
1111use super :: CString16 ;
1212
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.
1438#[ derive( Clone , Copy , Debug , Eq , PartialEq ) ]
1539pub enum FromSliceWithNulError {
1640 /// An invalid character was encountered before the end of the slice
@@ -337,6 +361,23 @@ impl CStr16 {
337361 Self :: from_u16_with_nul_unchecked ( slice:: from_raw_parts ( ptr, len + 1 ) )
338362 }
339363
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+
340381 /// Creates a `&CStr16` from a u16 slice, if the slice contains exactly
341382 /// one terminating null-byte and all chars are valid UCS-2 chars.
342383 pub fn from_u16_with_nul ( codes : & [ u16 ] ) -> Result < & Self , FromSliceWithNulError > {
@@ -369,6 +410,22 @@ impl CStr16 {
369410 & * ( codes as * const [ u16 ] as * const Self )
370411 }
371412
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+
372429 /// Creates a `&CStr16` from a [`Char16`] slice, if the slice is
373430 /// null-terminated and has no interior null characters.
374431 pub fn from_char16_with_nul ( chars : & [ Char16 ] ) -> Result < & Self , FromSliceWithNulError > {
@@ -734,6 +791,75 @@ mod tests {
734791 assert_eq ! ( s. num_bytes( ) , 8 ) ;
735792 }
736793
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+
737863 #[ test]
738864 fn test_cstr16_from_char16_with_nul ( ) {
739865 // Invalid: empty input.
0 commit comments