Skip to content

Commit 3f3acdf

Browse files
Auto merge of #149705 - paolobarbolini:string-extend-chunked, r=<try>
Optimize `Extend for String`
2 parents fbab541 + 207e484 commit 3f3acdf

File tree

1 file changed

+48
-4
lines changed

1 file changed

+48
-4
lines changed

library/alloc/src/string.rs

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2203,6 +2203,46 @@ impl String {
22032203
let slice = self.vec.leak();
22042204
unsafe { from_utf8_unchecked_mut(slice) }
22052205
}
2206+
2207+
// SAFETY: the `impl AsRef<str> for S` must be stable
2208+
#[cfg(not(no_global_oom_handling))]
2209+
unsafe fn extend_many_chunked<S: AsRef<str>, I: IntoIterator<Item = S>>(&mut self, iter: I) {
2210+
let mut iter = iter.into_iter();
2211+
2212+
let mut repeat = true;
2213+
while repeat {
2214+
let chunk = match iter.next_chunk::<8>() {
2215+
Ok(chunk) => chunk.into_iter(),
2216+
Err(partial_chunk) => {
2217+
repeat = false;
2218+
partial_chunk
2219+
}
2220+
};
2221+
2222+
// SAFETY: the caller must ensure that `impl AsRef<str> for S` is stable
2223+
unsafe { self.extend_many(chunk.as_slice()) }
2224+
}
2225+
}
2226+
2227+
// SAFETY: the `AsRef<str>` implementation must be stable
2228+
#[cfg(not(no_global_oom_handling))]
2229+
unsafe fn extend_many<S: AsRef<str>>(&mut self, vals: &[S]) {
2230+
let additional = vals.iter().fold(0usize, |a, s| a.saturating_add(s.as_ref().len()));
2231+
self.reserve(additional);
2232+
2233+
let mut spare = self.vec.spare_capacity_mut().as_mut_ptr().cast_init();
2234+
for val in vals {
2235+
let val = val.as_ref();
2236+
// TODO: document safety
2237+
unsafe { ptr::copy_nonoverlapping(val.as_ptr(), spare, val.len()) }
2238+
// TODO: document safety
2239+
spare = unsafe { spare.add(val.len()) };
2240+
}
2241+
2242+
let new_len = self.vec.len() + additional;
2243+
// SAFETY: the elements have just been initialized
2244+
unsafe { self.vec.set_len(new_len) }
2245+
}
22062246
}
22072247

22082248
impl FromUtf8Error {
@@ -2502,7 +2542,8 @@ impl<'a> Extend<&'a char> for String {
25022542
#[stable(feature = "rust1", since = "1.0.0")]
25032543
impl<'a> Extend<&'a str> for String {
25042544
fn extend<I: IntoIterator<Item = &'a str>>(&mut self, iter: I) {
2505-
iter.into_iter().for_each(move |s| self.push_str(s));
2545+
// SAFETY: `impl AsRef<str> for &str` is stable
2546+
unsafe { self.extend_many_chunked(iter) }
25062547
}
25072548

25082549
#[inline]
@@ -2515,15 +2556,17 @@ impl<'a> Extend<&'a str> for String {
25152556
#[stable(feature = "box_str2", since = "1.45.0")]
25162557
impl<A: Allocator> Extend<Box<str, A>> for String {
25172558
fn extend<I: IntoIterator<Item = Box<str, A>>>(&mut self, iter: I) {
2518-
iter.into_iter().for_each(move |s| self.push_str(&s));
2559+
// SAFETY: `impl AsRef<str> for Box<str, A>` is stable
2560+
unsafe { self.extend_many_chunked(iter) }
25192561
}
25202562
}
25212563

25222564
#[cfg(not(no_global_oom_handling))]
25232565
#[stable(feature = "extend_string", since = "1.4.0")]
25242566
impl Extend<String> for String {
25252567
fn extend<I: IntoIterator<Item = String>>(&mut self, iter: I) {
2526-
iter.into_iter().for_each(move |s| self.push_str(&s));
2568+
// SAFETY: `impl AsRef<str> for String` is stable
2569+
unsafe { self.extend_many_chunked(iter) }
25272570
}
25282571

25292572
#[inline]
@@ -2536,7 +2579,8 @@ impl Extend<String> for String {
25362579
#[stable(feature = "herd_cows", since = "1.19.0")]
25372580
impl<'a> Extend<Cow<'a, str>> for String {
25382581
fn extend<I: IntoIterator<Item = Cow<'a, str>>>(&mut self, iter: I) {
2539-
iter.into_iter().for_each(move |s| self.push_str(&s));
2582+
// SAFETY: `impl AsRef<str> for Cow<'a, str>` is stable
2583+
unsafe { self.extend_many_chunked(iter) }
25402584
}
25412585

25422586
#[inline]

0 commit comments

Comments
 (0)