Skip to content

Commit 4c072d3

Browse files
committed
Add ascii happy path
1 parent c9f19b4 commit 4c072d3

1 file changed

Lines changed: 42 additions & 11 deletions

File tree

library/alloc/src/string.rs

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ use core::iter::from_fn;
5050
use core::num::Saturating;
5151
#[cfg(not(no_global_oom_handling))]
5252
use core::ops::Add;
53-
#[cfg(not(no_global_oom_handling))]
54-
use core::ops::AddAssign;
5553
use core::ops::{self, Range, RangeBounds};
54+
#[cfg(not(no_global_oom_handling))]
55+
use core::ops::{AddAssign, ControlFlow};
5656
use core::str::pattern::{Pattern, Utf8Pattern};
5757
use core::{fmt, hash, ptr, slice};
5858

@@ -61,7 +61,7 @@ use crate::alloc::Allocator;
6161
#[cfg(not(no_global_oom_handling))]
6262
use crate::borrow::{Cow, ToOwned};
6363
use crate::boxed::Box;
64-
use crate::collections::{TryReserveError};
64+
use crate::collections::TryReserveError;
6565
#[cfg(not(no_global_oom_handling))]
6666
use crate::collections::VecDeque;
6767
use crate::str::{self, CharIndices, Chars, Utf8Error, from_utf8_unchecked_mut};
@@ -3610,6 +3610,25 @@ impl From<char> for String {
36103610
// In place case changes
36113611

36123612
impl String {
3613+
#[cfg(not(no_global_oom_handling))]
3614+
fn case_change_while_ascii<const MAKE_UPPER: bool>(&mut self) -> ControlFlow<usize> {
3615+
// SAFETY the as_bytes_mut is unsafe but we will only do ascii case change in place with it
3616+
unsafe {
3617+
self.as_bytes_mut().into_iter().enumerate().try_for_each(|(i, b)| {
3618+
if b.is_ascii() {
3619+
if MAKE_UPPER {
3620+
b.make_ascii_uppercase();
3621+
} else {
3622+
b.make_ascii_lowercase();
3623+
}
3624+
ControlFlow::Continue(())
3625+
} else {
3626+
ControlFlow::Break(i)
3627+
}
3628+
})
3629+
}
3630+
}
3631+
36133632
/// Converts this string to its uppercase equivalent in-place.
36143633
///
36153634
/// 'Uppercase' is defined according to the terms of the Unicode Derived Core Property
@@ -3656,7 +3675,10 @@ impl String {
36563675
#[cfg(not(no_global_oom_handling))]
36573676
#[unstable(feature = "string_make_uplowercase", issue = "135885")]
36583677
pub fn make_uppercase(&mut self) {
3659-
let mut wc = WriteChars::new(self);
3678+
let ControlFlow::Break(non_utf8_offset) = self.case_change_while_ascii::<true>() else {
3679+
return;
3680+
};
3681+
let mut wc = WriteChars::new(self, non_utf8_offset);
36603682
while let Some(l_c) = wc.pop() {
36613683
l_c.to_uppercase().for_each(|u_c| wc.write(u_c));
36623684
}
@@ -3710,11 +3732,19 @@ impl String {
37103732
#[cfg(not(no_global_oom_handling))]
37113733
#[unstable(feature = "string_make_uplowercase", issue = "135885")]
37123734
pub fn make_lowercase(&mut self) {
3735+
fn update_word_final(word_final_so_far: bool, u_c: char) -> bool {
3736+
u_c.is_cased() || (word_final_so_far && u_c.is_case_ignorable())
3737+
}
3738+
3739+
let ControlFlow::Break(non_utf8_offset) = self.case_change_while_ascii::<false>() else {
3740+
return;
3741+
};
37133742
// We will only update the streaming word_final detection if the str contains sigma
37143743
// because it requires table lookups that we consider expensive.
3715-
let has_sigma = self.contains('Σ');
3716-
let mut word_final_so_far = false;
3717-
let mut wc = WriteChars::new(self);
3744+
let has_sigma = self[non_utf8_offset..].contains('Σ');
3745+
let mut word_final_so_far =
3746+
has_sigma && self[..non_utf8_offset].chars().fold(false, update_word_final);
3747+
let mut wc = WriteChars::new(self, non_utf8_offset);
37183748
while let Some(u_c) = wc.pop() {
37193749
if u_c == 'Σ' {
37203750
if word_final_so_far && !crate::str::case_ignorable_then_cased(wc.rest().chars()) {
@@ -3727,8 +3757,7 @@ impl String {
37273757
u_c.to_lowercase().for_each(|l_c| wc.write(l_c));
37283758
}
37293759
if has_sigma {
3730-
word_final_so_far =
3731-
u_c.is_cased() || (word_final_so_far && u_c.is_case_ignorable());
3760+
word_final_so_far = update_word_final(word_final_so_far, u_c);
37323761
}
37333762
}
37343763
// SAFETY: At this point, none of the methods of wc panicked
@@ -3738,6 +3767,8 @@ impl String {
37383767
}
37393768
}
37403769

3770+
#[cfg(not(no_global_oom_handling))]
3771+
37413772
/// A helper for in place modification of strings, where we gradually "pop" characters,
37423773
/// hereby making room to write back to the string buffer
37433774
#[unstable(issue = "none", feature = "std_internals")]
@@ -3759,9 +3790,9 @@ struct WriteChars<'a> {
37593790

37603791
#[unstable(issue = "none", feature = "std_internals")]
37613792
impl<'a> WriteChars<'a> {
3762-
fn new(s: &'a mut String) -> Self {
3793+
fn new(s: &'a mut String, offset: usize) -> Self {
37633794
let v = core::mem::take(s).into_bytes();
3764-
WriteChars { s, v, write_offset: 0, read_offset: 0, buffer: VecDeque::new() }
3795+
WriteChars { s, v, write_offset: offset, read_offset: offset, buffer: VecDeque::new() }
37653796
}
37663797

37673798
fn rest(&self) -> &str {

0 commit comments

Comments
 (0)