diff --git a/libcxx/include/__string b/libcxx/include/__string --- a/libcxx/include/__string +++ b/libcxx/include/__string @@ -122,6 +122,8 @@ _Func(_LIBCPP_FUNC_VIS void basic_string<_CharType>::resize(size_type, value_type)) \ _Func(_LIBCPP_FUNC_VIS basic_string<_CharType>& basic_string<_CharType>::insert(size_type, basic_string const&, size_type, size_type)) +#define _LIBCPP_V2_STRING_EXTERN_TEMPLATE_LIST(_Func, _CharType) \ + _Func(_LIBCPP_FUNC_VIS basic_string<_CharType>& basic_string<_CharType>::__assign_str_noalias_external(value_type const*, size_type)) // char_traits diff --git a/libcxx/include/string b/libcxx/include/string --- a/libcxx/include/string +++ b/libcxx/include/string @@ -1542,6 +1542,14 @@ return __guess; } + static _LIBCPP_INLINE_VISIBILITY + size_type __recommend_cap(size_type __sz) _NOEXCEPT { + if (__sz < __min_cap) __sz = __min_cap; + return __align_it < sizeof(value_type) < __alignment + ? __alignment / sizeof(value_type) + : 1 > (__sz + 1); + } + inline void __init(const value_type* __s, size_type __sz, size_type __reserve); inline @@ -1576,6 +1584,11 @@ size_type __n_copy, size_type __n_del, size_type __n_add, const value_type* __p_new_stuff); + // Slow path for non-aliased string assignment. Requires: + // `__s` has a terminating zero at __s[__sz] + // `__s` does not reference (alias) any content in `this` + basic_string& __assign_str_noalias_external(const value_type* __s, size_type __sz); + _LIBCPP_INLINE_VISIBILITY void __erase_to_end(size_type __pos); @@ -2152,6 +2165,38 @@ __alloc_traits::deallocate(__alloc(), __get_long_pointer(), __get_long_cap()); } +template +basic_string<_CharT, _Traits, _Allocator>& +basic_string<_CharT, _Traits, _Allocator>::__assign_str_noalias_external( + const value_type* __s, size_type __sz) { + pointer __p; + size_type __cap; + if (!__is_long()) { + if (__sz < __min_cap) { + traits_type::copy(_VSTD::__to_address(__get_short_pointer()), __s, __sz + 1); + __set_short_size(__sz); + return *this; + } + __cap = __recommend_cap(__sz); + __p = __alloc_traits::allocate(__alloc(), __cap); + } else { + __cap = __get_long_cap(); + __p = __get_long_pointer(); + if (__sz >= __cap) { + size_type __new_cap = __recommend_cap(__sz); + pointer __new_p = __alloc_traits::allocate(__alloc(), __new_cap); + __alloc_traits::deallocate(__alloc(), __p, __cap); + __p = __new_p; + __cap = __new_cap; + } + } + traits_type::copy(_VSTD::__to_address(__p), __s, __sz + 1); + __set_long_pointer(__p); + __set_long_size(__sz); + __set_long_cap(__cap); + return *this; +} + template void basic_string<_CharT, _Traits, _Allocator>::__grow_by_and_replace @@ -2286,7 +2331,7 @@ __copy_assign_alloc(__str); const bool __str_is_long = __str.__is_long(); // Force single branch if (__is_long() || __str_is_long) { - return assign(__str.data(), __str.size()); + return __assign_str_noalias_external(__str.data(), __str.size()); } __r_.first().__r = __str.__r_.first().__r; } diff --git a/libcxx/src/string.cpp b/libcxx/src/string.cpp --- a/libcxx/src/string.cpp +++ b/libcxx/src/string.cpp @@ -23,6 +23,9 @@ _LIBCPP_STRING_EXTERN_TEMPLATE_LIST(_LIBCPP_EXTERN_TEMPLATE_DEFINE, char) _LIBCPP_STRING_EXTERN_TEMPLATE_LIST(_LIBCPP_EXTERN_TEMPLATE_DEFINE, wchar_t) +_LIBCPP_V2_STRING_EXTERN_TEMPLATE_LIST(_LIBCPP_EXTERN_TEMPLATE_DEFINE, char) +_LIBCPP_V2_STRING_EXTERN_TEMPLATE_LIST(_LIBCPP_EXTERN_TEMPLATE_DEFINE, wchar_t) + template string operator+, allocator >(char const*, string const&);