X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=library%2Fcore%2Fsrc%2Fffi%2Fc_str.rs;h=82e63a7fe1ddb6611cf3dc727136d2d7b235723f;hb=0e54d71e157693226c3c5f67d9755daf885714e2;hp=59066a33c965eecce361b9869e2b593d3b7a775a;hpb=fd3610ed2eba43ee8ae74838fafee27e1286b73e;p=rust.git diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 59066a33c96..82e63a7fe1d 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -2,6 +2,7 @@ use crate::cmp::Ordering; use crate::ffi::c_char; use crate::fmt::{self, Write}; +use crate::intrinsics; use crate::ops; use crate::slice; use crate::slice::memchr; @@ -384,21 +385,41 @@ pub fn from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, FromBytesWithNulError> #[must_use] #[stable(feature = "cstr_from_bytes", since = "1.10.0")] #[rustc_const_stable(feature = "const_cstr_unchecked", since = "1.59.0")] + #[rustc_allow_const_fn_unstable(const_eval_select)] pub const unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr { - // We're in a const fn, so this is the best we can do - debug_assert!(!bytes.is_empty() && bytes[bytes.len() - 1] == 0); - // SAFETY: Calling an inner function with the same prerequisites. - unsafe { Self::_from_bytes_with_nul_unchecked(bytes) } - } + fn rt_impl(bytes: &[u8]) -> &CStr { + // Chance at catching some UB at runtime with debug builds. + debug_assert!(!bytes.is_empty() && bytes[bytes.len() - 1] == 0); - #[inline] - const unsafe fn _from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr { - // SAFETY: Casting to CStr is safe because its internal representation - // is a [u8] too (safe only inside std). - // Dereferencing the obtained pointer is safe because it comes from a - // reference. Making a reference is then safe because its lifetime - // is bound by the lifetime of the given `bytes`. - unsafe { &*(bytes as *const [u8] as *const CStr) } + // SAFETY: Casting to CStr is safe because its internal representation + // is a [u8] too (safe only inside std). + // Dereferencing the obtained pointer is safe because it comes from a + // reference. Making a reference is then safe because its lifetime + // is bound by the lifetime of the given `bytes`. + unsafe { &*(bytes as *const [u8] as *const CStr) } + } + + const fn const_impl(bytes: &[u8]) -> &CStr { + // Saturating so that an empty slice panics in the assert with a good + // message, not here due to underflow. + let mut i = bytes.len().saturating_sub(1); + assert!(!bytes.is_empty() && bytes[i] == 0, "input was not nul-terminated"); + + // Ending null byte exists, skip to the rest. + while i != 0 { + i -= 1; + let byte = bytes[i]; + assert!(byte != 0, "input contained interior nul"); + } + + // SAFETY: See `rt_impl` cast. + unsafe { &*(bytes as *const [u8] as *const CStr) } + } + + // SAFETY: The const and runtime versions have identical behavior + // unless the safety contract of `from_bytes_with_nul_unchecked` is + // violated, which is UB. + unsafe { intrinsics::const_eval_select((bytes,), const_impl, rt_impl) } } /// Returns the inner pointer to this C string.