Skip to content

Commit 38ab135

Browse files
committed
alloc: specialize String::extend for slices of str
1 parent fa8eb7e commit 38ab135

File tree

1 file changed

+46
-1
lines changed

1 file changed

+46
-1
lines changed

library/alloc/src/string.rs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ use core::iter::FusedIterator;
4747
#[cfg(not(no_global_oom_handling))]
4848
use core::iter::from_fn;
4949
#[cfg(not(no_global_oom_handling))]
50+
use core::num::Saturating;
51+
#[cfg(not(no_global_oom_handling))]
5052
use core::ops::Add;
5153
#[cfg(not(no_global_oom_handling))]
5254
use core::ops::AddAssign;
@@ -1097,6 +1099,23 @@ impl String {
10971099
self.vec.extend_from_slice(string.as_bytes())
10981100
}
10991101

1102+
#[cfg(not(no_global_oom_handling))]
1103+
#[inline]
1104+
fn push_str_slice(&mut self, slice: &[&str]) {
1105+
// use saturating arithmetic to ensure that in the case of an overflow, reserve() throws OOM
1106+
let additional: Saturating<usize> = slice.iter().map(|x| Saturating(x.len())).sum();
1107+
self.reserve(additional.0);
1108+
let (ptr, len, cap) = core::mem::take(self).into_raw_parts();
1109+
unsafe {
1110+
let mut dst = ptr.add(len);
1111+
for new in slice {
1112+
core::ptr::copy_nonoverlapping(new.as_ptr(), dst, new.len());
1113+
dst = dst.add(new.len());
1114+
}
1115+
*self = String::from_raw_parts(ptr, len + additional.0, cap);
1116+
}
1117+
}
1118+
11001119
/// Copies elements from `src` range to the end of the string.
11011120
///
11021121
/// # Panics
@@ -2489,7 +2508,7 @@ impl<'a> Extend<&'a char> for String {
24892508
#[stable(feature = "rust1", since = "1.0.0")]
24902509
impl<'a> Extend<&'a str> for String {
24912510
fn extend<I: IntoIterator<Item = &'a str>>(&mut self, iter: I) {
2492-
iter.into_iter().for_each(move |s| self.push_str(s));
2511+
<I as SpecExtendStr>::spec_extend_into(iter, self)
24932512
}
24942513

24952514
#[inline]
@@ -2498,6 +2517,32 @@ impl<'a> Extend<&'a str> for String {
24982517
}
24992518
}
25002519

2520+
#[cfg(not(no_global_oom_handling))]
2521+
trait SpecExtendStr {
2522+
fn spec_extend_into(self, s: &mut String);
2523+
}
2524+
2525+
#[cfg(not(no_global_oom_handling))]
2526+
impl<'a, T: IntoIterator<Item = &'a str>> SpecExtendStr for T {
2527+
default fn spec_extend_into(self, target: &mut String) {
2528+
self.into_iter().for_each(move |s| target.push_str(s));
2529+
}
2530+
}
2531+
2532+
#[cfg(not(no_global_oom_handling))]
2533+
impl SpecExtendStr for [&str] {
2534+
fn spec_extend_into(self, target: &mut String) {
2535+
target.push_str_slice(&self);
2536+
}
2537+
}
2538+
2539+
#[cfg(not(no_global_oom_handling))]
2540+
impl<const N: usize> SpecExtendStr for [&str; N] {
2541+
fn spec_extend_into(self, target: &mut String) {
2542+
target.push_str_slice(&self[..]);
2543+
}
2544+
}
2545+
25012546
#[cfg(not(no_global_oom_handling))]
25022547
#[stable(feature = "box_str2", since = "1.45.0")]
25032548
impl<A: Allocator> Extend<Box<str, A>> for String {

0 commit comments

Comments
 (0)