Skip to content

Commit 0f7b2cd

Browse files
authored
Unrolled build for rust-lang#104353
Rollup merge of rust-lang#104353 - clarfonthey:cstr-bytes-iter, r=cuviper Add CStr::bytes iterator See rust-lang/libs-team#135 for an ACP. Since rust-lang/libs-team#134 was also accepted, this type is now `core::ffi::c_str::Bytes` instead of `core::ffi::CStrBytes`.
2 parents e69f14b + a38a556 commit 0f7b2cd

File tree

1 file changed

+91
-0
lines changed

1 file changed

+91
-0
lines changed

library/core/src/ffi/c_str.rs

+91
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@ use crate::error::Error;
55
use crate::ffi::c_char;
66
use crate::fmt;
77
use crate::intrinsics;
8+
use crate::iter::FusedIterator;
9+
use crate::marker::PhantomData;
810
use crate::ops;
911
use crate::ptr::addr_of;
12+
use crate::ptr::NonNull;
1013
use crate::slice;
1114
use crate::slice::memchr;
1215
use crate::str;
@@ -504,6 +507,13 @@ impl CStr {
504507
self.inner.as_ptr()
505508
}
506509

510+
/// We could eventually expose this publicly, if we wanted.
511+
#[inline]
512+
#[must_use]
513+
const fn as_non_null_ptr(&self) -> NonNull<c_char> {
514+
NonNull::from(&self.inner).as_non_null_ptr()
515+
}
516+
507517
/// Returns the length of `self`. Like C's `strlen`, this does not include the nul terminator.
508518
///
509519
/// > **Note**: This method is currently implemented as a constant-time
@@ -617,6 +627,26 @@ impl CStr {
617627
unsafe { &*(addr_of!(self.inner) as *const [u8]) }
618628
}
619629

630+
/// Iterates over the bytes in this C string.
631+
///
632+
/// The returned iterator will **not** contain the trailing nul terminator
633+
/// that this C string has.
634+
///
635+
/// # Examples
636+
///
637+
/// ```
638+
/// #![feature(cstr_bytes)]
639+
/// use std::ffi::CStr;
640+
///
641+
/// let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
642+
/// assert!(cstr.bytes().eq(*b"foo"));
643+
/// ```
644+
#[inline]
645+
#[unstable(feature = "cstr_bytes", issue = "112115")]
646+
pub fn bytes(&self) -> Bytes<'_> {
647+
Bytes::new(self)
648+
}
649+
620650
/// Yields a <code>&[str]</code> slice if the `CStr` contains valid UTF-8.
621651
///
622652
/// If the contents of the `CStr` are valid UTF-8 data, this
@@ -735,3 +765,64 @@ const unsafe fn const_strlen(ptr: *const c_char) -> usize {
735765
intrinsics::const_eval_select((ptr,), strlen_ct, strlen_rt)
736766
}
737767
}
768+
769+
/// An iterator over the bytes of a [`CStr`], without the nul terminator.
770+
///
771+
/// This struct is created by the [`bytes`] method on [`CStr`].
772+
/// See its documentation for more.
773+
///
774+
/// [`bytes`]: CStr::bytes
775+
#[must_use = "iterators are lazy and do nothing unless consumed"]
776+
#[unstable(feature = "cstr_bytes", issue = "112115")]
777+
#[derive(Clone, Debug)]
778+
pub struct Bytes<'a> {
779+
// since we know the string is nul-terminated, we only need one pointer
780+
ptr: NonNull<u8>,
781+
phantom: PhantomData<&'a u8>,
782+
}
783+
impl<'a> Bytes<'a> {
784+
#[inline]
785+
fn new(s: &'a CStr) -> Self {
786+
Self { ptr: s.as_non_null_ptr().cast(), phantom: PhantomData }
787+
}
788+
789+
#[inline]
790+
fn is_empty(&self) -> bool {
791+
// SAFETY: We uphold that the pointer is always valid to dereference
792+
// by starting with a valid C string and then never incrementing beyond
793+
// the nul terminator.
794+
unsafe { self.ptr.read() == 0 }
795+
}
796+
}
797+
798+
#[unstable(feature = "cstr_bytes", issue = "112115")]
799+
impl Iterator for Bytes<'_> {
800+
type Item = u8;
801+
802+
#[inline]
803+
fn next(&mut self) -> Option<u8> {
804+
// SAFETY: We only choose a pointer from a valid C string, which must
805+
// be non-null and contain at least one value. Since we always stop at
806+
// the nul terminator, which is guaranteed to exist, we can assume that
807+
// the pointer is non-null and valid. This lets us safely dereference
808+
// it and assume that adding 1 will create a new, non-null, valid
809+
// pointer.
810+
unsafe {
811+
let ret = self.ptr.read();
812+
if ret == 0 {
813+
None
814+
} else {
815+
self.ptr = self.ptr.offset(1);
816+
Some(ret)
817+
}
818+
}
819+
}
820+
821+
#[inline]
822+
fn size_hint(&self) -> (usize, Option<usize>) {
823+
if self.is_empty() { (0, Some(0)) } else { (1, None) }
824+
}
825+
}
826+
827+
#[unstable(feature = "cstr_bytes", issue = "112115")]
828+
impl FusedIterator for Bytes<'_> {}

0 commit comments

Comments
 (0)