Skip to content

Commit 7fc3183

Browse files
Rollup merge of #100291 - WaffleLapkin:cstr_const_methods, r=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 )
2 parents b7504d6 + cb02b64 commit 7fc3183

File tree

3 files changed

+37
-12
lines changed

3 files changed

+37
-12
lines changed

library/core/src/ffi/c_str.rs

+12-7
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,10 @@ enum FromBytesWithNulErrorKind {
120120
}
121121

122122
impl FromBytesWithNulError {
123-
fn interior_nul(pos: usize) -> FromBytesWithNulError {
123+
const fn interior_nul(pos: usize) -> FromBytesWithNulError {
124124
FromBytesWithNulError { kind: FromBytesWithNulErrorKind::InteriorNul(pos) }
125125
}
126-
fn not_nul_terminated() -> FromBytesWithNulError {
126+
const fn not_nul_terminated() -> FromBytesWithNulError {
127127
FromBytesWithNulError { kind: FromBytesWithNulErrorKind::NotNulTerminated }
128128
}
129129

@@ -294,7 +294,8 @@ impl CStr {
294294
/// ```
295295
///
296296
#[unstable(feature = "cstr_from_bytes_until_nul", issue = "95027")]
297-
pub fn from_bytes_until_nul(bytes: &[u8]) -> Result<&CStr, FromBytesUntilNulError> {
297+
#[rustc_const_unstable(feature = "cstr_from_bytes_until_nul", issue = "95027")]
298+
pub const fn from_bytes_until_nul(bytes: &[u8]) -> Result<&CStr, FromBytesUntilNulError> {
298299
let nul_pos = memchr::memchr(0, bytes);
299300
match nul_pos {
300301
Some(nul_pos) => {
@@ -343,7 +344,8 @@ impl CStr {
343344
/// assert!(cstr.is_err());
344345
/// ```
345346
#[stable(feature = "cstr_from_bytes", since = "1.10.0")]
346-
pub fn from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, FromBytesWithNulError> {
347+
#[rustc_const_unstable(feature = "const_cstr_methods", issue = "101719")]
348+
pub const fn from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, FromBytesWithNulError> {
347349
let nul_pos = memchr::memchr(0, bytes);
348350
match nul_pos {
349351
Some(nul_pos) if nul_pos + 1 == bytes.len() => {
@@ -493,7 +495,8 @@ impl CStr {
493495
#[must_use = "this returns the result of the operation, \
494496
without modifying the original"]
495497
#[stable(feature = "rust1", since = "1.0.0")]
496-
pub fn to_bytes(&self) -> &[u8] {
498+
#[rustc_const_unstable(feature = "const_cstr_methods", issue = "101719")]
499+
pub const fn to_bytes(&self) -> &[u8] {
497500
let bytes = self.to_bytes_with_nul();
498501
// SAFETY: to_bytes_with_nul returns slice with length at least 1
499502
unsafe { bytes.get_unchecked(..bytes.len() - 1) }
@@ -520,7 +523,8 @@ impl CStr {
520523
#[must_use = "this returns the result of the operation, \
521524
without modifying the original"]
522525
#[stable(feature = "rust1", since = "1.0.0")]
523-
pub fn to_bytes_with_nul(&self) -> &[u8] {
526+
#[rustc_const_unstable(feature = "const_cstr_methods", issue = "101719")]
527+
pub const fn to_bytes_with_nul(&self) -> &[u8] {
524528
// SAFETY: Transmuting a slice of `c_char`s to a slice of `u8`s
525529
// is safe on all supported targets.
526530
unsafe { &*(&self.inner as *const [c_char] as *const [u8]) }
@@ -543,7 +547,8 @@ impl CStr {
543547
/// assert_eq!(cstr.to_str(), Ok("foo"));
544548
/// ```
545549
#[stable(feature = "cstr_to_str", since = "1.4.0")]
546-
pub fn to_str(&self) -> Result<&str, str::Utf8Error> {
550+
#[rustc_const_unstable(feature = "const_cstr_methods", issue = "101719")]
551+
pub const fn to_str(&self) -> Result<&str, str::Utf8Error> {
547552
// N.B., when `CStr` is changed to perform the length check in `.to_bytes()`
548553
// instead of in `from_ptr()`, it may be worth considering if this should
549554
// be rewritten to do the UTF-8 check inline with the length calculation

library/core/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@
159159
#![feature(const_slice_from_ref)]
160160
#![feature(const_slice_index)]
161161
#![feature(const_is_char_boundary)]
162+
#![feature(const_cstr_methods)]
162163
//
163164
// Language features:
164165
#![feature(abi_unadjusted)]

library/core/src/slice/memchr.rs

+24-5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch
33

44
use crate::cmp;
5+
use crate::intrinsics;
56
use crate::mem;
67

78
const LO_USIZE: usize = usize::repeat_u8(0x01);
@@ -35,13 +36,31 @@ fn repeat_byte(b: u8) -> usize {
3536
/// Returns the first index matching the byte `x` in `text`.
3637
#[must_use]
3738
#[inline]
38-
pub fn memchr(x: u8, text: &[u8]) -> Option<usize> {
39-
// Fast path for small slices
40-
if text.len() < 2 * USIZE_BYTES {
41-
return text.iter().position(|elt| *elt == x);
39+
pub const fn memchr(x: u8, text: &[u8]) -> Option<usize> {
40+
#[inline]
41+
fn rt_impl(x: u8, text: &[u8]) -> Option<usize> {
42+
// Fast path for small slices
43+
if text.len() < 2 * USIZE_BYTES {
44+
return text.iter().position(|elt| *elt == x);
45+
}
46+
47+
memchr_general_case(x, text)
48+
}
49+
50+
const fn const_impl(x: u8, bytes: &[u8]) -> Option<usize> {
51+
let mut i = 0;
52+
while i < bytes.len() {
53+
if bytes[i] == x {
54+
return Some(i);
55+
}
56+
i += 1;
57+
}
58+
59+
None
4260
}
4361

44-
memchr_general_case(x, text)
62+
// SAFETY: The const and runtime versions have identical behavior
63+
unsafe { intrinsics::const_eval_select((x, text), const_impl, rt_impl) }
4564
}
4665

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

0 commit comments

Comments
 (0)