@@ -3,7 +3,10 @@ use crate::error::Error;
3
3
use crate :: ffi:: c_char;
4
4
use crate :: fmt;
5
5
use crate :: intrinsics;
6
+ use crate :: iter:: FusedIterator ;
7
+ use crate :: marker:: PhantomData ;
6
8
use crate :: ops;
9
+ use crate :: ptr:: NonNull ;
7
10
use crate :: slice;
8
11
use crate :: slice:: memchr;
9
12
use crate :: str;
@@ -595,6 +598,26 @@ impl CStr {
595
598
unsafe { & * ( & self . inner as * const [ c_char ] as * const [ u8 ] ) }
596
599
}
597
600
601
+ /// Iterates over the bytes in this C string.
602
+ ///
603
+ /// The returned iterator will **not** contain the trailing nul terminator
604
+ /// that this C string has.
605
+ ///
606
+ /// # Examples
607
+ ///
608
+ /// ```
609
+ /// #![feature(cstr_bytes)]
610
+ /// use std::ffi::CStr;
611
+ ///
612
+ /// let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
613
+ /// assert!(cstr.bytes().eq(*b"foo"));
614
+ /// ```
615
+ #[ inline]
616
+ #[ unstable( feature = "cstr_bytes" , issue = "112115" ) ]
617
+ pub fn bytes ( & self ) -> CStrBytes < ' _ > {
618
+ CStrBytes :: new ( self )
619
+ }
620
+
598
621
/// Yields a <code>&[str]</code> slice if the `CStr` contains valid UTF-8.
599
622
///
600
623
/// If the contents of the `CStr` are valid UTF-8 data, this
@@ -675,3 +698,71 @@ impl AsRef<CStr> for CStr {
675
698
self
676
699
}
677
700
}
701
+
702
+ /// An iterator over the bytes of a [`CStr`], without the nul terminator.
703
+ ///
704
+ /// This struct is created by the [`bytes`] method on [`CStr`].
705
+ /// See its documentation for more.
706
+ ///
707
+ /// [`bytes`]: CStr::bytes
708
+ #[ must_use = "iterators are lazy and do nothing unless consumed" ]
709
+ #[ unstable( feature = "cstr_bytes" , issue = "112115" ) ]
710
+ #[ derive( Clone , Debug ) ]
711
+ pub struct CStrBytes < ' a > {
712
+ // since we know the string is nul-terminated, we only need one pointer
713
+ ptr : NonNull < u8 > ,
714
+ phantom : PhantomData < & ' a u8 > ,
715
+ }
716
+ impl < ' a > CStrBytes < ' a > {
717
+ #[ inline]
718
+ fn new ( s : & ' a CStr ) -> Self {
719
+ Self {
720
+ // SAFETY: Because we have a valid reference to the string, we know
721
+ // that its pointer is non-null.
722
+ ptr : unsafe { NonNull :: new_unchecked ( s. inner . as_ptr ( ) as * const u8 as * mut u8 ) } ,
723
+ phantom : PhantomData ,
724
+ }
725
+ }
726
+
727
+ #[ inline]
728
+ fn is_empty ( & self ) -> bool {
729
+ // SAFETY: We uphold that the pointer is always valid to dereference
730
+ // by starting with a valid C string and then never incrementing beyond
731
+ // the nul terminator.
732
+ unsafe { * self . ptr . as_ref ( ) == 0 }
733
+ }
734
+ }
735
+
736
+ #[ unstable( feature = "cstr_bytes" , issue = "112115" ) ]
737
+ impl Iterator for CStrBytes < ' _ > {
738
+ type Item = u8 ;
739
+
740
+ #[ inline]
741
+ fn next ( & mut self ) -> Option < u8 > {
742
+ // SAFETY: We only choose a pointer from a valid C string, which must
743
+ // be non-null and contain at least one value. Since we always stop at
744
+ // the nul terminator, which is guaranteed to exist, we can assume that
745
+ // the pointer is non-null and valid. This lets us safely dereference
746
+ // it and assume that adding 1 will create a new, non-null, valid
747
+ // pointer.
748
+ unsafe {
749
+ intrinsics:: assume ( !self . ptr . as_ptr ( ) . is_null ( ) ) ;
750
+
751
+ let ret = * self . ptr . as_ref ( ) ;
752
+ if ret == 0 {
753
+ None
754
+ } else {
755
+ self . ptr = NonNull :: new_unchecked ( self . ptr . as_ptr ( ) . offset ( 1 ) ) ;
756
+ Some ( ret)
757
+ }
758
+ }
759
+ }
760
+
761
+ #[ inline]
762
+ fn size_hint ( & self ) -> ( usize , Option < usize > ) {
763
+ if self . is_empty ( ) { ( 0 , Some ( 0 ) ) } else { ( 1 , None ) }
764
+ }
765
+ }
766
+
767
+ #[ unstable( feature = "cstr_bytes" , issue = "112115" ) ]
768
+ impl FusedIterator for CStrBytes < ' _ > { }
0 commit comments