Skip to content

Commit 026103b

Browse files
committed
Optimise replacen 1-ascii when count >= len
1 parent 104784a commit 026103b

File tree

2 files changed

+16
-15
lines changed

2 files changed

+16
-15
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
~2x speedup inline, ~4-22x for heap.
77
- Optimise `StrExt::to_lowercase_smolstr`, `StrExt::to_uppercase_smolstr` ~2x speedup inline, ~5-50x for heap.
88
- Optimise `StrExt::replace_smolstr`, `StrExt::replacen_smolstr` for single ascii replace.
9-
~3x speedup inline, ~1.8x for heap (len=50).
9+
~3.7x speedup inline, ~2.4x for heap.
1010

1111
## 0.3.2 - 2024-10-23
1212

src/lib.rs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -714,11 +714,21 @@ impl StrExt for str {
714714
}
715715

716716
#[inline]
717-
fn replacen_smolstr(&self, from: &str, to: &str, count: usize) -> SmolStr {
717+
fn replacen_smolstr(&self, from: &str, to: &str, mut count: usize) -> SmolStr {
718718
// Fast path for replacing a single ASCII character with another inline.
719719
if let [from_u8] = from.as_bytes() {
720720
if let [to_u8] = to.as_bytes() {
721-
return replacen_1_ascii(self, *from_u8, *to_u8, count);
721+
return match self.len() <= count {
722+
true => replacen_1_ascii(self, |b| if b == *from_u8 { *to_u8 } else { b }),
723+
_ => replacen_1_ascii(self, |b| {
724+
if b == *from_u8 && count != 0 {
725+
count -= 1;
726+
*to_u8
727+
} else {
728+
b
729+
}
730+
}),
731+
};
722732
}
723733
}
724734

@@ -739,28 +749,19 @@ impl StrExt for str {
739749
}
740750

741751
#[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+
fn replacen_1_ascii(src: &str, mut map: impl FnMut(u8) -> u8) -> SmolStr {
752753
if src.len() <= INLINE_CAP {
753754
let mut buf = [0u8; INLINE_CAP];
754755
for (idx, b) in src.as_bytes().iter().enumerate() {
755-
buf[idx] = ascii_replace(b);
756+
buf[idx] = map(*b);
756757
}
757758
SmolStr(Repr::Inline {
758759
// SAFETY: `len` is in bounds
759760
len: unsafe { InlineSize::transmute_from_u8(src.len() as u8) },
760761
buf,
761762
})
762763
} else {
763-
let out = src.as_bytes().iter().map(ascii_replace).collect();
764+
let out = src.as_bytes().iter().map(|b| map(*b)).collect();
764765
// SAFETY: We replaced ascii with ascii on valid utf8 strings.
765766
unsafe { String::from_utf8_unchecked(out).into() }
766767
}

0 commit comments

Comments
 (0)