@@ -51,6 +51,9 @@ pub enum FromSliceWithNulError {
5151
5252 /// The slice was not null-terminated
5353 NotNulTerminated ,
54+
55+ /// Slice is not aligned to a 16-bit boundary.
56+ NotAligned ,
5457}
5558
5659impl Display for FromSliceWithNulError {
@@ -59,6 +62,7 @@ impl Display for FromSliceWithNulError {
5962 Self :: InvalidChar ( usize) => write ! ( f, "invalid character at index {usize}" ) ,
6063 Self :: InteriorNul ( usize) => write ! ( f, "interior null character at index {usize}" ) ,
6164 Self :: NotNulTerminated => write ! ( f, "not null-terminated" ) ,
65+ Self :: NotAligned => write ! ( f, "slice is not aligned to a 16-bit boundary" ) ,
6266 }
6367 }
6468}
@@ -505,7 +509,7 @@ impl CStr16 {
505509 Self :: from_u16_with_nul ( & buf[ ..index + 1 ] ) . map_err ( |err| match err {
506510 FromSliceWithNulError :: InvalidChar ( p) => FromStrWithBufError :: InvalidChar ( p) ,
507511 FromSliceWithNulError :: InteriorNul ( p) => FromStrWithBufError :: InteriorNul ( p) ,
508- FromSliceWithNulError :: NotNulTerminated => {
512+ FromSliceWithNulError :: NotNulTerminated | FromSliceWithNulError :: NotAligned => {
509513 unreachable ! ( )
510514 }
511515 } )
@@ -533,9 +537,61 @@ impl CStr16 {
533537 FromSliceWithNulError :: InvalidChar ( v) => UnalignedCStr16Error :: InvalidChar ( v) ,
534538 FromSliceWithNulError :: InteriorNul ( v) => UnalignedCStr16Error :: InteriorNul ( v) ,
535539 FromSliceWithNulError :: NotNulTerminated => UnalignedCStr16Error :: NotNulTerminated ,
540+ // input is aligned
541+ FromSliceWithNulError :: NotAligned => unreachable ! ( ) ,
536542 } )
537543 }
538544
545+ /// Creates a `&CStr16` from a byte slice.
546+ ///
547+ /// The byte slice is reinterpreted as a `u16` slice and validated as a
548+ /// null-terminated UCS-2 string. The slice must satisfy the following
549+ /// requirements:
550+ ///
551+ /// - Its length must be even (every `u16` is two bytes).
552+ /// - Its starting address must be 2-byte aligned.
553+ /// - It must be null-terminated with no interior null characters.
554+ /// - All `u16` values must be valid UCS-2 characters.
555+ ///
556+ /// # Errors
557+ ///
558+ /// See [`FromSliceWithNulError`].
559+ ///
560+ /// # Examples
561+ ///
562+ /// ```
563+ /// use uefi::CStr16;
564+ ///
565+ /// let mut aligned_buf = [0u16; 3];
566+ /// // "AB\0" as native-endian u16 bytes
567+ /// aligned_buf[0] = b'A' as u16;
568+ /// aligned_buf[1] = b'B' as u16;
569+ /// aligned_buf[2] = 0;
570+ ///
571+ /// let bytes: &[u8] = unsafe {
572+ /// core::slice::from_raw_parts(aligned_buf.as_ptr().cast::<u8>(), 6)
573+ /// };
574+ ///
575+ /// let s = CStr16::from_bytes_with_nul(bytes).unwrap();
576+ /// assert_eq!(s.to_u16_slice_with_nul(), &[b'A' as u16, b'B' as u16, 0]);
577+ /// ```
578+ pub fn from_bytes_with_nul ( bytes : & [ u8 ] ) -> Result < & Self , FromSliceWithNulError > {
579+ if !bytes. len ( ) . is_multiple_of ( size_of :: < Char16 > ( ) ) || !bytes. ends_with ( & [ 0 , 0 ] ) {
580+ return Err ( FromSliceWithNulError :: NotNulTerminated ) ;
581+ }
582+
583+ // Unlikely: Most UEFI buffers are 8 byte aligned
584+ if bytes. as_ptr ( ) . align_offset ( 2 ) != 0 {
585+ return Err ( FromSliceWithNulError :: NotAligned ) ;
586+ }
587+
588+ // Safety: length is even and pointer is 2-byte aligned.
589+ let u16_slice =
590+ unsafe { slice:: from_raw_parts ( bytes. as_ptr ( ) . cast :: < u16 > ( ) , bytes. len ( ) / 2 ) } ;
591+
592+ Self :: from_u16_with_nul ( u16_slice)
593+ }
594+
539595 /// Returns the inner pointer to this C16 string.
540596 #[ must_use]
541597 pub const fn as_ptr ( & self ) -> * const Char16 {
@@ -1124,4 +1180,68 @@ mod tests {
11241180 assert ! ( String :: from( "test" ) . eq_str_until_nul( input) ) ;
11251181 assert ! ( "test" . eq_str_until_nul( input) ) ;
11261182 }
1183+
1184+ #[ test]
1185+ fn test_cstr16_from_bytes_with_nul ( ) {
1186+ // Valid: "AB\0"
1187+ let aligned: & [ u16 ] = & [ b'A' as u16 , b'B' as u16 , 0 ] ;
1188+ let bytes = unsafe { slice:: from_raw_parts ( aligned. as_ptr ( ) . cast :: < u8 > ( ) , 6 ) } ;
1189+ let s = CStr16 :: from_bytes_with_nul ( bytes) . unwrap ( ) ;
1190+ assert_eq ! ( s. to_u16_slice_with_nul( ) , & [ 65 , 66 , 0 ] ) ;
1191+
1192+ // Invalid: odd number of bytes.
1193+ let aligned: & [ u16 ] = & [ b'A' as u16 , 0 ] ;
1194+ let bytes = unsafe { slice:: from_raw_parts ( aligned. as_ptr ( ) . cast :: < u8 > ( ) , 3 ) } ;
1195+ assert_eq ! (
1196+ CStr16 :: from_bytes_with_nul( bytes) ,
1197+ Err ( FromSliceWithNulError :: NotNulTerminated )
1198+ ) ;
1199+
1200+ // Invalid: not null-terminated (last u16 is not NUL).
1201+ let aligned: & [ u16 ] = & [ b'A' as u16 , b'B' as u16 ] ;
1202+ let bytes = unsafe { slice:: from_raw_parts ( aligned. as_ptr ( ) . cast :: < u8 > ( ) , 4 ) } ;
1203+ assert_eq ! (
1204+ CStr16 :: from_bytes_with_nul( bytes) ,
1205+ Err ( FromSliceWithNulError :: NotNulTerminated )
1206+ ) ;
1207+
1208+ // Invalid: only the high byte of the terminator is zero (half-null).
1209+ let aligned: & [ u16 ] = & [ b'A' as u16 , 0x0100 ] ;
1210+ let bytes = unsafe { slice:: from_raw_parts ( aligned. as_ptr ( ) . cast :: < u8 > ( ) , 4 ) } ;
1211+ assert_eq ! (
1212+ CStr16 :: from_bytes_with_nul( bytes) ,
1213+ Err ( FromSliceWithNulError :: NotNulTerminated )
1214+ ) ;
1215+
1216+ // Invalid: interior null.
1217+ let aligned: & [ u16 ] = & [ b'A' as u16 , 0 , b'B' as u16 , 0 ] ;
1218+ let bytes = unsafe { slice:: from_raw_parts ( aligned. as_ptr ( ) . cast :: < u8 > ( ) , 8 ) } ;
1219+ assert_eq ! (
1220+ CStr16 :: from_bytes_with_nul( bytes) ,
1221+ Err ( FromSliceWithNulError :: InteriorNul ( 1 ) )
1222+ ) ;
1223+
1224+ // Invalid: invalid UCS-2 character.
1225+ let aligned: & [ u16 ] = & [ 0xd800 , 0 ] ;
1226+ let bytes = unsafe { slice:: from_raw_parts ( aligned. as_ptr ( ) . cast :: < u8 > ( ) , 4 ) } ;
1227+ assert_eq ! (
1228+ CStr16 :: from_bytes_with_nul( bytes) ,
1229+ Err ( FromSliceWithNulError :: InvalidChar ( 0 ) )
1230+ ) ;
1231+
1232+ // Invalid: misaligned pointer. We simulate this by taking a byte slice
1233+ // starting one byte into an aligned u16 buffer.
1234+ let aligned: & [ u16 ] = & [ b'A' as u16 , 0 , 0 ] ;
1235+ let bytes = unsafe { slice:: from_raw_parts ( aligned. as_ptr ( ) . cast :: < u8 > ( ) . add ( 1 ) , 4 ) } ;
1236+ assert_eq ! (
1237+ CStr16 :: from_bytes_with_nul( bytes) ,
1238+ Err ( FromSliceWithNulError :: NotAligned )
1239+ ) ;
1240+
1241+ // Edge case: empty string (just a null terminator).
1242+ let aligned: & [ u16 ] = & [ 0 ] ;
1243+ let bytes = unsafe { slice:: from_raw_parts ( aligned. as_ptr ( ) . cast :: < u8 > ( ) , 2 ) } ;
1244+ let s = CStr16 :: from_bytes_with_nul ( bytes) . unwrap ( ) ;
1245+ assert ! ( s. is_empty( ) ) ;
1246+ }
11271247}
0 commit comments