@@ -5,8 +5,11 @@ use crate::error::Error;
55use crate :: ffi:: c_char;
66use crate :: fmt;
77use crate :: intrinsics;
8+ use crate :: iter:: FusedIterator ;
9+ use crate :: marker:: PhantomData ;
810use crate :: ops;
911use crate :: ptr:: addr_of;
12+ use crate :: ptr:: NonNull ;
1013use crate :: slice;
1114use crate :: slice:: memchr;
1215use crate :: str;
@@ -617,6 +620,26 @@ impl CStr {
617620 unsafe { & * ( addr_of ! ( self . inner) as * const [ u8 ] ) }
618621 }
619622
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+
620643 /// Yields a <code>&[str]</code> slice if the `CStr` contains valid UTF-8.
621644 ///
622645 /// 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 {
735758 intrinsics:: const_eval_select ( ( ptr, ) , strlen_ct, strlen_rt)
736759 }
737760}
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