X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=library%2Fcore%2Fsrc%2Fffi%2Fc_str.rs;h=82e63a7fe1ddb6611cf3dc727136d2d7b235723f;hb=0e54d71e157693226c3c5f67d9755daf885714e2;hp=ee9baf811e29c099c70d18323978f655a98e65a6;hpb=6277ac2fb8bf97cd910a0a841c6924b246d32c44;p=rust.git diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index ee9baf811e2..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; @@ -65,9 +66,9 @@ /// extern "C" { fn my_string() -> *const c_char; } /// /// fn my_string_safe() -> String { -/// unsafe { -/// CStr::from_ptr(my_string()).to_string_lossy().into_owned() -/// } +/// let cstr = unsafe { CStr::from_ptr(my_string()) }; +/// // Get copy-on-write Cow<'_, str>, then guarantee a freshly-owned String allocation +/// String::from_utf8_lossy(cstr.to_bytes()).to_string() /// } /// /// println!("string: {}", my_string_safe()); @@ -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.