@@ -214,6 +214,8 @@ impl CStr {
214214 /// * The memory referenced by the returned `CStr` must not be mutated for
215215 /// the duration of lifetime `'a`.
216216 ///
217+ /// * The nul terminator must be within `isize::MAX` from `ptr`
218+ ///
217219 /// > **Note**: This operation is intended to be a 0-cost cast but it is
218220 /// > currently implemented with an up-front calculation of the length of
219221 /// > the string. This is not guaranteed to always be the case.
@@ -259,42 +261,16 @@ impl CStr {
259261 #[ rustc_const_unstable( feature = "const_cstr_from_ptr" , issue = "113219" ) ]
260262 pub const unsafe fn from_ptr < ' a > ( ptr : * const c_char ) -> & ' a CStr {
261263 // SAFETY: The caller has provided a pointer that points to a valid C
262- // string with a NUL terminator of size less than `isize::MAX`, whose
263- // content remain valid and doesn't change for the lifetime of the
264- // returned `CStr`.
265- //
266- // Thus computing the length is fine (a NUL byte exists), the call to
267- // from_raw_parts is safe because we know the length is at most `isize::MAX`, meaning
268- // the call to `from_bytes_with_nul_unchecked` is correct.
264+ // string with a NUL terminator less than `isize::MAX` from `ptr`.
265+ let len = unsafe { const_strlen ( ptr ) } ;
266+
267+ // SAFETY: The caller has provided a valid pointer with length less than
268+ // `isize::MAX`, so `from_raw_parts` is safe. The content remains valid
269+ // and doesn't change for the lifetime of the returned `CStr`. This
270+ // means the call to `from_bytes_with_nul_unchecked` is correct.
269271 //
270272 // The cast from c_char to u8 is ok because a c_char is always one byte.
271- unsafe {
272- const fn strlen_ct ( s : * const c_char ) -> usize {
273- let mut len = 0 ;
274-
275- // SAFETY: Outer caller has provided a pointer to a valid C string.
276- while unsafe { * s. add ( len) } != 0 {
277- len += 1 ;
278- }
279-
280- len
281- }
282-
283- // `inline` is necessary for codegen to see strlen.
284- #[ inline]
285- fn strlen_rt ( s : * const c_char ) -> usize {
286- extern "C" {
287- /// Provided by libc or compiler_builtins.
288- fn strlen ( s : * const c_char ) -> usize ;
289- }
290-
291- // SAFETY: Outer caller has provided a pointer to a valid C string.
292- unsafe { strlen ( s) }
293- }
294-
295- let len = intrinsics:: const_eval_select ( ( ptr, ) , strlen_ct, strlen_rt) ;
296- Self :: from_bytes_with_nul_unchecked ( slice:: from_raw_parts ( ptr. cast ( ) , len + 1 ) )
297- }
273+ unsafe { Self :: from_bytes_with_nul_unchecked ( slice:: from_raw_parts ( ptr. cast ( ) , len + 1 ) ) }
298274 }
299275
300276 /// Creates a C string wrapper from a byte slice with any number of nuls.
@@ -516,6 +492,34 @@ impl CStr {
516492 self . inner . as_ptr ( )
517493 }
518494
495+ /// Returns the length of `self`. Like C's `strlen`, this does not include the nul terminator.
496+ ///
497+ /// > **Note**: This method is currently implemented as a constant-time
498+ /// > cast, but it is planned to alter its definition in the future to
499+ /// > perform the length calculation whenever this method is called.
500+ ///
501+ /// # Examples
502+ ///
503+ /// ```
504+ /// #![feature(cstr_count_bytes)]
505+ ///
506+ /// use std::ffi::CStr;
507+ ///
508+ /// let cstr = CStr::from_bytes_with_nul(b"foo\0").unwrap();
509+ /// assert_eq!(cstr.count_bytes(), 3);
510+ ///
511+ /// let cstr = CStr::from_bytes_with_nul(b"\0").unwrap();
512+ /// assert_eq!(cstr.count_bytes(), 0);
513+ /// ```
514+ #[ inline]
515+ #[ must_use]
516+ #[ doc( alias( "len" , "strlen" ) ) ]
517+ #[ unstable( feature = "cstr_count_bytes" , issue = "114441" ) ]
518+ #[ rustc_const_unstable( feature = "const_cstr_from_ptr" , issue = "113219" ) ]
519+ pub const fn count_bytes ( & self ) -> usize {
520+ self . inner . len ( ) - 1
521+ }
522+
519523 /// Returns `true` if `self.to_bytes()` has a length of 0.
520524 ///
521525 /// # Examples
@@ -682,3 +686,37 @@ impl AsRef<CStr> for CStr {
682686 self
683687 }
684688}
689+
690+ /// Calculate the length of a nul-terminated string. Defers to C's `strlen` when possible.
691+ ///
692+ /// # Safety
693+ ///
694+ /// The pointer must point to a valid buffer that contains a NUL terminator. The NUL must be
695+ /// located within `isize::MAX` from `ptr`.
696+ #[ inline]
697+ const unsafe fn const_strlen ( ptr : * const c_char ) -> usize {
698+ const fn strlen_ct ( s : * const c_char ) -> usize {
699+ let mut len = 0 ;
700+
701+ // SAFETY: Outer caller has provided a pointer to a valid C string.
702+ while unsafe { * s. add ( len) } != 0 {
703+ len += 1 ;
704+ }
705+
706+ len
707+ }
708+
709+ #[ inline]
710+ fn strlen_rt ( s : * const c_char ) -> usize {
711+ extern "C" {
712+ /// Provided by libc or compiler_builtins.
713+ fn strlen ( s : * const c_char ) -> usize ;
714+ }
715+
716+ // SAFETY: Outer caller has provided a pointer to a valid C string.
717+ unsafe { strlen ( s) }
718+ }
719+
720+ // SAFETY: the two functions always provide equivalent functionality
721+ unsafe { intrinsics:: const_eval_select ( ( ptr, ) , strlen_ct, strlen_rt) }
722+ }
0 commit comments