@@ -214,6 +214,8 @@ impl CStr {
214
214
/// * The memory referenced by the returned `CStr` must not be mutated for
215
215
/// the duration of lifetime `'a`.
216
216
///
217
+ /// * The nul terminator must be within `isize::MAX` from `ptr`
218
+ ///
217
219
/// > **Note**: This operation is intended to be a 0-cost cast but it is
218
220
/// > currently implemented with an up-front calculation of the length of
219
221
/// > the string. This is not guaranteed to always be the case.
@@ -259,42 +261,16 @@ impl CStr {
259
261
#[ rustc_const_unstable( feature = "const_cstr_from_ptr" , issue = "113219" ) ]
260
262
pub const unsafe fn from_ptr < ' a > ( ptr : * const c_char ) -> & ' a CStr {
261
263
// 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.
269
271
//
270
272
// 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 ) ) }
298
274
}
299
275
300
276
/// Creates a C string wrapper from a byte slice with any number of nuls.
@@ -516,6 +492,34 @@ impl CStr {
516
492
self . inner . as_ptr ( )
517
493
}
518
494
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
+
519
523
/// Returns `true` if `self.to_bytes()` has a length of 0.
520
524
///
521
525
/// # Examples
@@ -682,3 +686,37 @@ impl AsRef<CStr> for CStr {
682
686
self
683
687
}
684
688
}
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