@@ -715,6 +715,13 @@ impl StrExt for str {
715715
716716 #[ inline]
717717 fn replacen_smolstr ( & self , from : & str , to : & str , count : usize ) -> SmolStr {
718+ // Fast path for replacing a single ASCII character with another inline.
719+ if let [ from_u8] = from. as_bytes ( ) {
720+ if let [ to_u8] = to. as_bytes ( ) {
721+ return replacen_1_ascii ( self , * from_u8, * to_u8, count) ;
722+ }
723+ }
724+
718725 let mut result = SmolStrBuilder :: new ( ) ;
719726 let mut last_end = 0 ;
720727 for ( start, part) in self . match_indices ( from) . take ( count) {
@@ -731,6 +738,34 @@ impl StrExt for str {
731738 }
732739}
733740
741+ #[ inline]
742+ fn replacen_1_ascii ( src : & str , from : u8 , to : u8 , count : usize ) -> SmolStr {
743+ let mut replaced = 0 ;
744+ let mut ascii_replace = |b : & u8 | {
745+ if * b == from && replaced != count {
746+ replaced += 1 ;
747+ to
748+ } else {
749+ * b
750+ }
751+ } ;
752+ if src. len ( ) <= INLINE_CAP {
753+ let mut buf = [ 0u8 ; INLINE_CAP ] ;
754+ for ( idx, b) in src. as_bytes ( ) . iter ( ) . enumerate ( ) {
755+ buf[ idx] = ascii_replace ( b) ;
756+ }
757+ SmolStr ( Repr :: Inline {
758+ // SAFETY: `len` is in bounds
759+ len : unsafe { InlineSize :: transmute_from_u8 ( src. len ( ) as u8 ) } ,
760+ buf,
761+ } )
762+ } else {
763+ let out = src. as_bytes ( ) . iter ( ) . map ( ascii_replace) . collect ( ) ;
764+ // SAFETY: We replaced ascii with ascii on valid utf8 strings.
765+ unsafe { String :: from_utf8_unchecked ( out) . into ( ) }
766+ }
767+ }
768+
734769/// Inline version of std fn `convert_while_ascii`. `s` must have len <= 23.
735770#[ inline]
736771fn inline_convert_while_ascii ( s : & str , convert : fn ( & u8 ) -> u8 ) -> ( [ u8 ; INLINE_CAP ] , & str ) {
0 commit comments