@@ -11,7 +11,7 @@ use core::borrow::{Borrow, BorrowMut};
11
11
use core:: iter:: FusedIterator ;
12
12
#[ stable( feature = "rust1" , since = "1.0.0" ) ]
13
13
pub use core:: str:: pattern;
14
- use core:: str:: pattern:: { DoubleEndedSearcher , Pattern , ReverseSearcher , Searcher } ;
14
+ use core:: str:: pattern:: { DoubleEndedSearcher , Pattern , ReverseSearcher , Searcher , Utf8Pattern } ;
15
15
#[ stable( feature = "encode_utf16" , since = "1.8.0" ) ]
16
16
pub use core:: str:: EncodeUtf16 ;
17
17
#[ stable( feature = "split_ascii_whitespace" , since = "1.34.0" ) ]
@@ -268,35 +268,17 @@ impl str {
268
268
#[ stable( feature = "rust1" , since = "1.0.0" ) ]
269
269
#[ inline]
270
270
pub fn replace < P : Pattern > ( & self , from : P , to : & str ) -> String {
271
- let mut capacity = 0 ;
272
-
273
- #[ inline]
274
- fn get_minimum_result_capacity ( input_len : usize , from_len : usize , to_len : usize ) -> usize {
275
- // check if output is going to be at least as long as input.
276
- if from_len <= to_len {
277
- return input_len;
271
+ // Fast path for ASCII to ASCII case.
272
+ if let Some ( from_byte) = match from. as_utf8_pattern ( ) {
273
+ Some ( Utf8Pattern :: Slice ( [ from_byte] ) ) => Some ( * from_byte) ,
274
+ Some ( Utf8Pattern :: Char ( c) ) => c. as_ascii ( ) . map ( |ascii_char| ascii_char. to_u8 ( ) ) ,
275
+ _ => None ,
276
+ } {
277
+ if let [ to_byte] = to. as_bytes ( ) {
278
+ return unsafe { replace_ascii ( self . as_bytes ( ) , from_byte, * to_byte) } ;
278
279
}
279
- // lower bound where we have the maximum number of matches:
280
- // max_n_matches = len(input) / len(from)
281
- // capacity = max_n_matches * to_utf8_len
282
- let max_n_matches = input_len. checked_div ( from_len) . unwrap_or ( 0 ) ;
283
- return max_n_matches * to_len;
284
280
}
285
-
286
- // Path for patterns that can be represented as utf8 bytes (str, char etc.).
287
- if let Some ( from_as_utf8) = from. as_utf8_bytes ( ) {
288
- let from_utf8_len = from_as_utf8. len ( ) ;
289
- let to_utf8_len = to. as_bytes ( ) . len ( ) ;
290
- // Fast path for ascii
291
- if from_utf8_len == 1 && to_utf8_len == 1 {
292
- return unsafe {
293
- replace_ascii ( & self . as_bytes ( ) , from_as_utf8[ 0 ] , to. as_bytes ( ) [ 0 ] )
294
- } ;
295
- }
296
- capacity = get_minimum_result_capacity ( self . bytes ( ) . len ( ) , from_utf8_len, to_utf8_len) ;
297
- }
298
-
299
- let mut result = String :: with_capacity ( capacity) ;
281
+ let mut result = String :: new ( ) ;
300
282
let mut last_end = 0 ;
301
283
for ( start, part) in self . match_indices ( from) {
302
284
result. push_str ( unsafe { self . get_unchecked ( last_end..start) } ) ;
@@ -692,6 +674,8 @@ fn convert_while_ascii(b: &[u8], convert: fn(&u8) -> u8) -> Vec<u8> {
692
674
#[ inline]
693
675
#[ cfg( not( test) ) ]
694
676
#[ cfg( not( no_global_oom_handling) ) ]
677
+ /// Faster implementation of string replacement for ASCII to ASCII cases.
678
+ /// Should produce fast vectorized code.
695
679
unsafe fn replace_ascii ( utf8_bytes : & [ u8 ] , from : u8 , to : u8 ) -> String {
696
680
let result: Vec < u8 > = utf8_bytes. iter ( ) . map ( |b| if * b == from { to } else { * b } ) . collect ( ) ;
697
681
// SAFETY: We replaced ascii with ascii on valid utf8 strings.
0 commit comments