Skip to content

Commit a8132c3

Browse files
committed
rust: optimization bounds checking for CStr
Signed-off-by: Gary Guo <[email protected]>
1 parent 211e411 commit a8132c3

File tree

2 files changed

+17
-10
lines changed

2 files changed

+17
-10
lines changed

rust/kernel/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
const_mut_refs,
2121
const_panic,
2222
const_raw_ptr_deref,
23+
const_unreachable_unchecked,
2324
try_reserve
2425
)]
2526
#![deny(clippy::complexity)]

rust/kernel/str.rs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,18 @@ impl CStr {
5959
/// Returns the length of this string excluding `NUL`.
6060
#[inline]
6161
pub const fn len(&self) -> usize {
62-
self.0.len() - 1
62+
self.len_with_nul() - 1
6363
}
6464

6565
/// Returns the length of this string with `NUL`.
6666
#[inline]
6767
pub const fn len_with_nul(&self) -> usize {
68+
// SAFETY: This is one of the invariant of `CStr`.
69+
// We add a `unreachable_unchecked` here to hint the optimizer that
70+
// the value returned from this function is non-zero.
71+
if self.0.is_empty() {
72+
unsafe { core::hint::unreachable_unchecked() };
73+
}
6874
self.0.len()
6975
}
7076

@@ -99,7 +105,9 @@ impl CStr {
99105
return Err(CStrConvertError::NotNulTerminated);
100106
}
101107
let mut i = 0;
102-
while i < bytes.len() - 1 {
108+
// `i + 1 < bytes.len()` allows LLVM to optimize away bounds checking,
109+
// while it couldn't optimize away bounds checks for `i < bytes.len() - 1`.
110+
while i + 1 < bytes.len() {
103111
if bytes[i] == 0 {
104112
return Err(CStrConvertError::InteriorNul);
105113
}
@@ -148,7 +156,7 @@ impl CStr {
148156
/// Convert the string to a byte slice without the trailing 0 byte.
149157
#[inline]
150158
pub fn as_bytes(&self) -> &[u8] {
151-
&self.0[..self.0.len() - 1]
159+
&self.0[..self.len()]
152160
}
153161

154162
/// Convert the string to a byte slice containing the trailing 0 byte.
@@ -178,14 +186,12 @@ impl Index<ops::RangeFrom<usize>> for CStr {
178186
type Output = CStr;
179187

180188
#[inline]
189+
// Clippy false positive
190+
#[allow(clippy::unnecessary_operation)]
181191
fn index(&self, index: ops::RangeFrom<usize>) -> &Self::Output {
182-
assert!(
183-
index.start <= self.len(),
184-
"range start index {} out of range for slice of length {}",
185-
index.start,
186-
self.len()
187-
);
188-
// SAFETY: We just checked the length.
192+
// Delegate bounds checking to slice.
193+
&self.as_bytes()[index.start..];
194+
// SAFETY: We just checked the bounds.
189195
unsafe { Self::from_bytes_with_nul_unchecked(&self.0[index.start..]) }
190196
}
191197
}

0 commit comments

Comments
 (0)