Skip to content

Commit

Permalink
Rollup merge of rust-lang#100291 - WaffleLapkin:cstr_const_methods, r…
Browse files Browse the repository at this point in the history
…=oli-obk

constify some `CStr` methods

This PR marks the following public APIs as `const`:
```rust
impl CStr {
    // feature(const_cstr_from_bytes)
    pub const fn from_bytes_until_nul(bytes: &[u8]) -> Result<&CStr, FromBytesUntilNulError>;
    pub const fn from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, FromBytesWithNulError>;

    // feature(const_cstr_to_bytes)
    pub const fn to_bytes(&self) -> &[u8];
    pub const fn to_bytes_with_nul(&self) -> &[u8];
    pub const fn to_str(&self) -> Result<&str, str::Utf8Error>;
}
```

r? ``@oli-obk`` (use of `const_eval_select` :P )
cc ``@mina86`` (you've asked for this <3 )
  • Loading branch information
GuillaumeGomez authored Sep 12, 2022
2 parents 9853343 + cb02b64 commit 2670093
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 12 deletions.
19 changes: 12 additions & 7 deletions library/core/src/ffi/c_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,10 @@ enum FromBytesWithNulErrorKind {
}

impl FromBytesWithNulError {
fn interior_nul(pos: usize) -> FromBytesWithNulError {
const fn interior_nul(pos: usize) -> FromBytesWithNulError {
FromBytesWithNulError { kind: FromBytesWithNulErrorKind::InteriorNul(pos) }
}
fn not_nul_terminated() -> FromBytesWithNulError {
const fn not_nul_terminated() -> FromBytesWithNulError {
FromBytesWithNulError { kind: FromBytesWithNulErrorKind::NotNulTerminated }
}

Expand Down Expand Up @@ -294,7 +294,8 @@ impl CStr {
/// ```
///
#[unstable(feature = "cstr_from_bytes_until_nul", issue = "95027")]
pub fn from_bytes_until_nul(bytes: &[u8]) -> Result<&CStr, FromBytesUntilNulError> {
#[rustc_const_unstable(feature = "cstr_from_bytes_until_nul", issue = "95027")]
pub const fn from_bytes_until_nul(bytes: &[u8]) -> Result<&CStr, FromBytesUntilNulError> {
let nul_pos = memchr::memchr(0, bytes);
match nul_pos {
Some(nul_pos) => {
Expand Down Expand Up @@ -343,7 +344,8 @@ impl CStr {
/// assert!(cstr.is_err());
/// ```
#[stable(feature = "cstr_from_bytes", since = "1.10.0")]
pub fn from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, FromBytesWithNulError> {
#[rustc_const_unstable(feature = "const_cstr_methods", issue = "101719")]
pub const fn from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, FromBytesWithNulError> {
let nul_pos = memchr::memchr(0, bytes);
match nul_pos {
Some(nul_pos) if nul_pos + 1 == bytes.len() => {
Expand Down Expand Up @@ -493,7 +495,8 @@ impl CStr {
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn to_bytes(&self) -> &[u8] {
#[rustc_const_unstable(feature = "const_cstr_methods", issue = "101719")]
pub const fn to_bytes(&self) -> &[u8] {
let bytes = self.to_bytes_with_nul();
// SAFETY: to_bytes_with_nul returns slice with length at least 1
unsafe { bytes.get_unchecked(..bytes.len() - 1) }
Expand All @@ -520,7 +523,8 @@ impl CStr {
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn to_bytes_with_nul(&self) -> &[u8] {
#[rustc_const_unstable(feature = "const_cstr_methods", issue = "101719")]
pub const fn to_bytes_with_nul(&self) -> &[u8] {
// SAFETY: Transmuting a slice of `c_char`s to a slice of `u8`s
// is safe on all supported targets.
unsafe { &*(&self.inner as *const [c_char] as *const [u8]) }
Expand All @@ -543,7 +547,8 @@ impl CStr {
/// assert_eq!(cstr.to_str(), Ok("foo"));
/// ```
#[stable(feature = "cstr_to_str", since = "1.4.0")]
pub fn to_str(&self) -> Result<&str, str::Utf8Error> {
#[rustc_const_unstable(feature = "const_cstr_methods", issue = "101719")]
pub const fn to_str(&self) -> Result<&str, str::Utf8Error> {
// N.B., when `CStr` is changed to perform the length check in `.to_bytes()`
// instead of in `from_ptr()`, it may be worth considering if this should
// be rewritten to do the UTF-8 check inline with the length calculation
Expand Down
1 change: 1 addition & 0 deletions library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@
#![feature(const_slice_from_ref)]
#![feature(const_slice_index)]
#![feature(const_is_char_boundary)]
#![feature(const_cstr_methods)]
//
// Language features:
#![feature(abi_unadjusted)]
Expand Down
29 changes: 24 additions & 5 deletions library/core/src/slice/memchr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch

use crate::cmp;
use crate::intrinsics;
use crate::mem;

const LO_USIZE: usize = usize::repeat_u8(0x01);
Expand Down Expand Up @@ -35,13 +36,31 @@ fn repeat_byte(b: u8) -> usize {
/// Returns the first index matching the byte `x` in `text`.
#[must_use]
#[inline]
pub fn memchr(x: u8, text: &[u8]) -> Option<usize> {
// Fast path for small slices
if text.len() < 2 * USIZE_BYTES {
return text.iter().position(|elt| *elt == x);
pub const fn memchr(x: u8, text: &[u8]) -> Option<usize> {
#[inline]
fn rt_impl(x: u8, text: &[u8]) -> Option<usize> {
// Fast path for small slices
if text.len() < 2 * USIZE_BYTES {
return text.iter().position(|elt| *elt == x);
}

memchr_general_case(x, text)
}

const fn const_impl(x: u8, bytes: &[u8]) -> Option<usize> {
let mut i = 0;
while i < bytes.len() {
if bytes[i] == x {
return Some(i);
}
i += 1;
}

None
}

memchr_general_case(x, text)
// SAFETY: The const and runtime versions have identical behavior
unsafe { intrinsics::const_eval_select((x, text), const_impl, rt_impl) }
}

fn memchr_general_case(x: u8, text: &[u8]) -> Option<usize> {
Expand Down

0 comments on commit 2670093

Please sign in to comment.