@@ -2,6 +2,7 @@ use crate::ascii;
22use crate :: cmp:: Ordering ;
33use crate :: ffi:: c_char;
44use crate :: fmt:: { self , Write } ;
5+ use crate :: intrinsics;
56use crate :: ops;
67use crate :: slice;
78use crate :: slice:: memchr;
@@ -384,21 +385,41 @@ impl CStr {
384385 #[ must_use]
385386 #[ stable( feature = "cstr_from_bytes" , since = "1.10.0" ) ]
386387 #[ rustc_const_stable( feature = "const_cstr_unchecked" , since = "1.59.0" ) ]
388+ #[ rustc_allow_const_fn_unstable( const_eval_select) ]
387389 pub const unsafe fn from_bytes_with_nul_unchecked ( bytes : & [ u8 ] ) -> & CStr {
388- // We're in a const fn, so this is the best we can do
389- debug_assert ! ( !bytes. is_empty( ) && bytes[ bytes. len( ) - 1 ] == 0 ) ;
390- // SAFETY: Calling an inner function with the same prerequisites.
391- unsafe { Self :: _from_bytes_with_nul_unchecked ( bytes) }
392- }
390+ fn rt_impl ( bytes : & [ u8 ] ) -> & CStr {
391+ // Chance at catching some UB at runtime with debug builds.
392+ debug_assert ! ( !bytes. is_empty( ) && bytes[ bytes. len( ) - 1 ] == 0 ) ;
393393
394- #[ inline]
395- const unsafe fn _from_bytes_with_nul_unchecked ( bytes : & [ u8 ] ) -> & CStr {
396- // SAFETY: Casting to CStr is safe because its internal representation
397- // is a [u8] too (safe only inside std).
398- // Dereferencing the obtained pointer is safe because it comes from a
399- // reference. Making a reference is then safe because its lifetime
400- // is bound by the lifetime of the given `bytes`.
401- unsafe { & * ( bytes as * const [ u8 ] as * const CStr ) }
394+ // SAFETY: Casting to CStr is safe because its internal representation
395+ // is a [u8] too (safe only inside std).
396+ // Dereferencing the obtained pointer is safe because it comes from a
397+ // reference. Making a reference is then safe because its lifetime
398+ // is bound by the lifetime of the given `bytes`.
399+ unsafe { & * ( bytes as * const [ u8 ] as * const CStr ) }
400+ }
401+
402+ const fn const_impl ( bytes : & [ u8 ] ) -> & CStr {
403+ // Saturating so that an empty slice panics in the assert with a good
404+ // message, not here due to underflow.
405+ let mut i = bytes. len ( ) . saturating_sub ( 1 ) ;
406+ assert ! ( !bytes. is_empty( ) && bytes[ i] == 0 , "input was not nul-terminated" ) ;
407+
408+ // Ending null byte exists, skip to the rest.
409+ while i != 0 {
410+ i -= 1 ;
411+ let byte = bytes[ i] ;
412+ assert ! ( byte != 0 , "input contained interior nul" ) ;
413+ }
414+
415+ // SAFETY: See `rt_impl` cast.
416+ unsafe { & * ( bytes as * const [ u8 ] as * const CStr ) }
417+ }
418+
419+ // SAFETY: The const and runtime versions have identical behavior
420+ // unless the safety contract of `from_bytes_with_nul_unchecked` is
421+ // violated, which is UB.
422+ unsafe { intrinsics:: const_eval_select ( ( bytes, ) , const_impl, rt_impl) }
402423 }
403424
404425 /// Returns the inner pointer to this C string.
0 commit comments