From 38ab135542d45b882f7e2bdaf07cd3a04673b2ec Mon Sep 17 00:00:00 2001 From: binarycat Date: Fri, 5 Dec 2025 14:45:30 -0600 Subject: [PATCH] alloc: specialize String::extend for slices of str --- library/alloc/src/string.rs | 47 ++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 4a2689e01ff17..69dce9b1de17c 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -47,6 +47,8 @@ use core::iter::FusedIterator; #[cfg(not(no_global_oom_handling))] use core::iter::from_fn; #[cfg(not(no_global_oom_handling))] +use core::num::Saturating; +#[cfg(not(no_global_oom_handling))] use core::ops::Add; #[cfg(not(no_global_oom_handling))] use core::ops::AddAssign; @@ -1097,6 +1099,23 @@ impl String { self.vec.extend_from_slice(string.as_bytes()) } + #[cfg(not(no_global_oom_handling))] + #[inline] + fn push_str_slice(&mut self, slice: &[&str]) { + // use saturating arithmetic to ensure that in the case of an overflow, reserve() throws OOM + let additional: Saturating = slice.iter().map(|x| Saturating(x.len())).sum(); + self.reserve(additional.0); + let (ptr, len, cap) = core::mem::take(self).into_raw_parts(); + unsafe { + let mut dst = ptr.add(len); + for new in slice { + core::ptr::copy_nonoverlapping(new.as_ptr(), dst, new.len()); + dst = dst.add(new.len()); + } + *self = String::from_raw_parts(ptr, len + additional.0, cap); + } + } + /// Copies elements from `src` range to the end of the string. /// /// # Panics @@ -2489,7 +2508,7 @@ impl<'a> Extend<&'a char> for String { #[stable(feature = "rust1", since = "1.0.0")] impl<'a> Extend<&'a str> for String { fn extend>(&mut self, iter: I) { - iter.into_iter().for_each(move |s| self.push_str(s)); + ::spec_extend_into(iter, self) } #[inline] @@ -2498,6 +2517,32 @@ impl<'a> Extend<&'a str> for String { } } +#[cfg(not(no_global_oom_handling))] +trait SpecExtendStr { + fn spec_extend_into(self, s: &mut String); +} + +#[cfg(not(no_global_oom_handling))] +impl<'a, T: IntoIterator> SpecExtendStr for T { + default fn spec_extend_into(self, target: &mut String) { + self.into_iter().for_each(move |s| target.push_str(s)); + } +} + +#[cfg(not(no_global_oom_handling))] +impl SpecExtendStr for [&str] { + fn spec_extend_into(self, target: &mut String) { + target.push_str_slice(&self); + } +} + +#[cfg(not(no_global_oom_handling))] +impl SpecExtendStr for [&str; N] { + fn spec_extend_into(self, target: &mut String) { + target.push_str_slice(&self[..]); + } +} + #[cfg(not(no_global_oom_handling))] #[stable(feature = "box_str2", since = "1.45.0")] impl Extend> for String {