@@ -5,8 +5,11 @@ use crate::error::Error;
5
5
use crate :: ffi:: c_char;
6
6
use crate :: fmt;
7
7
use crate :: intrinsics;
8
+ use crate :: iter:: FusedIterator ;
9
+ use crate :: marker:: PhantomData ;
8
10
use crate :: ops;
9
11
use crate :: ptr:: addr_of;
12
+ use crate :: ptr:: NonNull ;
10
13
use crate :: slice;
11
14
use crate :: slice:: memchr;
12
15
use crate :: str;
@@ -617,6 +620,26 @@ impl CStr {
617
620
unsafe { & * ( addr_of ! ( self . inner) as * const [ u8 ] ) }
618
621
}
619
622
623
+ /// Iterates over the bytes in this C string.
624
+ ///
625
+ /// The returned iterator will **not** contain the trailing nul terminator
626
+ /// that this C string has.
627
+ ///
628
+ /// # Examples
629
+ ///
630
+ /// ```
631
+ /// #![feature(cstr_bytes)]
632
+ /// use std::ffi::CStr;
633
+ ///
634
+ /// let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
635
+ /// assert!(cstr.bytes().eq(*b"foo"));
636
+ /// ```
637
+ #[ inline]
638
+ #[ unstable( feature = "cstr_bytes" , issue = "112115" ) ]
639
+ pub fn bytes ( & self ) -> Bytes < ' _ > {
640
+ Bytes :: new ( self )
641
+ }
642
+
620
643
/// Yields a <code>&[str]</code> slice if the `CStr` contains valid UTF-8.
621
644
///
622
645
/// If the contents of the `CStr` are valid UTF-8 data, this
@@ -735,3 +758,69 @@ const unsafe fn const_strlen(ptr: *const c_char) -> usize {
735
758
intrinsics:: const_eval_select ( ( ptr, ) , strlen_ct, strlen_rt)
736
759
}
737
760
}
761
+
762
+ /// An iterator over the bytes of a [`CStr`], without the nul terminator.
763
+ ///
764
+ /// This struct is created by the [`bytes`] method on [`CStr`].
765
+ /// See its documentation for more.
766
+ ///
767
+ /// [`bytes`]: CStr::bytes
768
+ #[ must_use = "iterators are lazy and do nothing unless consumed" ]
769
+ #[ unstable( feature = "cstr_bytes" , issue = "112115" ) ]
770
+ #[ derive( Clone , Debug ) ]
771
+ pub struct Bytes < ' a > {
772
+ // since we know the string is nul-terminated, we only need one pointer
773
+ ptr : NonNull < u8 > ,
774
+ phantom : PhantomData < & ' a u8 > ,
775
+ }
776
+ impl < ' a > Bytes < ' a > {
777
+ #[ inline]
778
+ fn new ( s : & ' a CStr ) -> Self {
779
+ Self {
780
+ // SAFETY: Because we have a valid reference to the string, we know
781
+ // that its pointer is non-null.
782
+ ptr : unsafe { NonNull :: new_unchecked ( s. as_ptr ( ) as * const u8 as * mut u8 ) } ,
783
+ phantom : PhantomData ,
784
+ }
785
+ }
786
+
787
+ #[ inline]
788
+ fn is_empty ( & self ) -> bool {
789
+ // SAFETY: We uphold that the pointer is always valid to dereference
790
+ // by starting with a valid C string and then never incrementing beyond
791
+ // the nul terminator.
792
+ unsafe { * self . ptr . as_ref ( ) == 0 }
793
+ }
794
+ }
795
+
796
+ #[ unstable( feature = "cstr_bytes" , issue = "112115" ) ]
797
+ impl Iterator for Bytes < ' _ > {
798
+ type Item = u8 ;
799
+
800
+ #[ inline]
801
+ fn next ( & mut self ) -> Option < u8 > {
802
+ // SAFETY: We only choose a pointer from a valid C string, which must
803
+ // be non-null and contain at least one value. Since we always stop at
804
+ // the nul terminator, which is guaranteed to exist, we can assume that
805
+ // the pointer is non-null and valid. This lets us safely dereference
806
+ // it and assume that adding 1 will create a new, non-null, valid
807
+ // pointer.
808
+ unsafe {
809
+ let ret = * self . ptr . as_ref ( ) ;
810
+ if ret == 0 {
811
+ None
812
+ } else {
813
+ self . ptr = NonNull :: new_unchecked ( self . ptr . as_ptr ( ) . offset ( 1 ) ) ;
814
+ Some ( ret)
815
+ }
816
+ }
817
+ }
818
+
819
+ #[ inline]
820
+ fn size_hint ( & self ) -> ( usize , Option < usize > ) {
821
+ if self . is_empty ( ) { ( 0 , Some ( 0 ) ) } else { ( 1 , None ) }
822
+ }
823
+ }
824
+
825
+ #[ unstable( feature = "cstr_bytes" , issue = "112115" ) ]
826
+ impl FusedIterator for Bytes < ' _ > { }
0 commit comments