@@ -3,7 +3,10 @@ use crate::error::Error;
33use crate :: ffi:: c_char;
44use crate :: fmt;
55use crate :: intrinsics;
6+ use crate :: iter:: FusedIterator ;
7+ use crate :: marker:: PhantomData ;
68use crate :: ops;
9+ use crate :: ptr:: NonNull ;
710use crate :: slice;
811use crate :: slice:: memchr;
912use crate :: str;
@@ -595,6 +598,26 @@ impl CStr {
595598 unsafe { & * ( & self . inner as * const [ c_char ] as * const [ u8 ] ) }
596599 }
597600
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+
598621 /// Yields a <code>&[str]</code> slice if the `CStr` contains valid UTF-8.
599622 ///
600623 /// If the contents of the `CStr` are valid UTF-8 data, this
@@ -675,3 +698,71 @@ impl AsRef<CStr> for CStr {
675698 self
676699 }
677700}
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