diff --git a/libcxx/include/string b/libcxx/include/string index 2b3ba6d2d9b62..18a3748943715 100644 --- a/libcxx/include/string +++ b/libcxx/include/string @@ -1440,24 +1440,48 @@ public: template ::value, int> = 0> _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(_ForwardIterator __first, _ForwardIterator __last) { - size_type __sz = size(); - size_type __cap = capacity(); - size_type __n = static_cast(std::distance(__first, __last)); + const size_type __size = size(); + const size_type __cap = capacity(); + const size_type __n = static_cast(std::distance(__first, __last)); if (__n == 0) return *this; - if (__string_is_trivial_iterator_v<_ForwardIterator> && !__addr_in_range(*__first)) { - if (__cap - __sz < __n) - __grow_by_without_replace(__cap, __sz + __n - __cap, __sz, __sz, 0); - __annotate_increase(__n); - auto __end = __copy_non_overlapping_range(__first, __last, std::__to_address(__get_pointer() + __sz)); - traits_type::assign(*__end, value_type()); - __set_size(__sz + __n); - return *this; + if (__n > __cap - __size) { + __long __buffer = __allocate_long_buffer(__alloc_, __get_amortized_growth_capacity(__size + __n)); + __buffer.__size_ = __size + __n; + auto __guard = std::__make_exception_guard([&] { + __alloc_traits::deallocate(__alloc_, __buffer.__data_, __buffer.__cap_ * __endian_factor); + }); + auto __end = __copy_non_overlapping_range(__first, __last, std::__to_address(__buffer.__data_) + __size); + traits_type::assign(*__end, _CharT()); + traits_type::copy(std::__to_address(__buffer.__data_), data(), __size); + __guard.__complete(); + __reset_internal_buffer(__buffer); } else { - const basic_string __temp(__first, __last, __alloc_); - return append(__temp.data(), __temp.size()); + _CharT* const __ptr = std::__to_address(__get_pointer()); +# ifndef _LIBCPP_CXX03_LANG + if constexpr (__libcpp_is_contiguous_iterator<_ForwardIterator>::value && + is_same>::value) { + traits_type::move(__ptr + __size, std::__to_address(__first), __last - __first); + } else if constexpr (__has_bidirectional_iterator_category<_ForwardIterator>::value) { + auto __dest = __ptr + __size + __n; + while (__first != __last) { + --__last; + --__dest; + traits_type::assign(*__dest, *__last); + } + } else +# endif + { + _CharT __first_val = *__first; + ++__first; + __copy_non_overlapping_range(__first, __last, __ptr + __size + 1); + traits_type::assign(__ptr[__size], __first_val); + } + traits_type::assign(__ptr[__size + __n], _CharT()); + __set_size(__size + __n); } + return *this; } # if _LIBCPP_STD_VER >= 23 diff --git a/libcxx/test/benchmarks/containers/string.bench.cpp b/libcxx/test/benchmarks/containers/string.bench.cpp index 2484ec8fd955f..6408b2312f022 100644 --- a/libcxx/test/benchmarks/containers/string.bench.cpp +++ b/libcxx/test/benchmarks/containers/string.bench.cpp @@ -11,7 +11,10 @@ #include #include #include +#include +#include #include +#include #include #include "../CartesianBenchmarks.h" @@ -122,6 +125,26 @@ static void BM_StringResizeAndOverwrite(benchmark::State& state) { } BENCHMARK(BM_StringResizeAndOverwrite); +template +static void BM_StringAppend(benchmark::State& state) { + std::string orig_str = "This is some data so the string isn't empty"; + std::string str = orig_str; + + const char to_append_str[] = "This is a long string to make sure append does some work"; + Container to_append; + std::copy(std::begin(to_append_str), std::end(to_append_str), std::back_inserter(to_append)); + + for (auto _ : state) { + benchmark::DoNotOptimize(str); + str.append(to_append.begin(), to_append.end()); + benchmark::DoNotOptimize(str); + str.resize(orig_str.size()); + } +} +BENCHMARK(BM_StringAppend>); +BENCHMARK(BM_StringAppend>); +BENCHMARK(BM_StringAppend>); + enum class Length { Empty, Small, Large, Huge }; struct AllLengths : EnumValuesAsTuple { static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};