@@ -1884,6 +1884,8 @@ private:
18841884 operator ==(const basic_string<char , char_traits<char >, _Alloc>& __lhs,
18851885 const basic_string<char , char_traits<char >, _Alloc>& __rhs) _NOEXCEPT;
18861886
1887+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __shrink_or_extend (size_type __target_capacity);
1888+
18871889 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS bool
18881890 __is_long () const _NOEXCEPT {
18891891 if (__libcpp_is_constant_evaluated () && __builtin_constant_p (__rep_.__l .__is_long_ )) {
@@ -2078,21 +2080,6 @@ private:
20782080#endif
20792081 }
20802082
2081- // Disable ASan annotations and enable them again when going out of scope. It is assumed that the string is in a valid
2082- // state at that point, so `size()` can be called safely.
2083- struct [[__nodiscard__]] __annotation_guard {
2084- __annotation_guard (const __annotation_guard&) = delete ;
2085- __annotation_guard& operator =(const __annotation_guard&) = delete ;
2086-
2087- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __annotation_guard (basic_string& __str) : __str_ (__str) {
2088- __str_.__annotate_delete ();
2089- }
2090-
2091- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__annotation_guard () { __str_.__annotate_new (__str_.size ()); }
2092-
2093- basic_string& __str_;
2094- };
2095-
20962083 template <size_type __a>
20972084 static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type __align_it (size_type __s) _NOEXCEPT {
20982085 return (__s + (__a - 1 )) & ~(__a - 1 );
@@ -3371,16 +3358,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::re
33713358 if (__requested_capacity <= capacity ())
33723359 return ;
33733360
3374- __annotation_guard __g (*this );
3375- auto __allocation = std::__allocate_at_least (__alloc_, __recommend (__requested_capacity) + 1 );
3376- auto __size = size ();
3377- __begin_lifetime (__allocation.ptr , __allocation.count );
3378- traits_type::copy (std::__to_address (__allocation.ptr ), data (), __size + 1 );
3379- if (__is_long ())
3380- __alloc_traits::deallocate (__alloc_, __get_long_pointer (), __get_long_size () + 1 );
3381- __set_long_cap (__allocation.count );
3382- __set_long_size (__size);
3383- __set_long_pointer (__allocation.ptr );
3361+ __shrink_or_extend (__recommend (__requested_capacity));
33843362}
33853363
33863364template <class _CharT , class _Traits , class _Allocator >
@@ -3389,46 +3367,69 @@ inline _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocat
33893367 if (__target_capacity == capacity ())
33903368 return ;
33913369
3392- _LIBCPP_ASSERT_INTERNAL (__is_long (), " Trying to shrink small string" );
3370+ __shrink_or_extend (__target_capacity);
3371+ }
33933372
3394- // We're a long string and we're shrinking into the small buffer.
3395- if (__fits_in_sso (__target_capacity)) {
3396- __annotation_guard __g (*this );
3397- auto __ptr = __get_long_pointer ();
3398- auto __size = __get_long_size ();
3399- auto __cap = __get_long_cap ();
3400- traits_type::copy (std::__to_address (__get_short_pointer ()), std::__to_address (__ptr), __size + 1 );
3401- __set_short_size (__size);
3402- __alloc_traits::deallocate (__alloc_, __ptr, __cap);
3403- return ;
3404- }
3373+ template <class _CharT , class _Traits , class _Allocator >
3374+ inline _LIBCPP_CONSTEXPR_SINCE_CXX20 void
3375+ basic_string<_CharT, _Traits, _Allocator>::__shrink_or_extend (size_type __target_capacity) {
3376+ __annotate_delete ();
3377+ auto __guard = std::__make_scope_guard (__annotate_new_size (*this ));
3378+ size_type __cap = capacity ();
3379+ size_type __sz = size ();
34053380
3381+ pointer __new_data, __p;
3382+ bool __was_long, __now_long;
3383+ if (__fits_in_sso (__target_capacity)) {
3384+ __was_long = true ;
3385+ __now_long = false ;
3386+ __new_data = __get_short_pointer ();
3387+ __p = __get_long_pointer ();
3388+ } else {
3389+ if (__target_capacity > __cap) {
3390+ // Extend
3391+ // - called from reserve should propagate the exception thrown.
3392+ auto __allocation = std::__allocate_at_least (__alloc_, __target_capacity + 1 );
3393+ __new_data = __allocation.ptr ;
3394+ __target_capacity = __allocation.count - 1 ;
3395+ } else {
3396+ // Shrink
3397+ // - called from shrink_to_fit should not throw.
3398+ // - called from reserve may throw but is not required to.
34063399#if _LIBCPP_HAS_EXCEPTIONS
3407- try {
3400+ try {
34083401#endif // _LIBCPP_HAS_EXCEPTIONS
3409- __annotation_guard __g (*this );
3410- auto __size = size ();
3411- auto __allocation = std::__allocate_at_least (__alloc_, __target_capacity + 1 );
3412-
3413- // The Standard mandates shrink_to_fit() does not increase the capacity.
3414- // With equal capacity keep the existing buffer. This avoids extra work
3415- // due to swapping the elements.
3416- if (__allocation.count - 1 > capacity ()) {
3417- __alloc_traits::deallocate (__alloc_, __allocation.ptr , __allocation.count );
3418- return ;
3419- }
3420-
3421- __begin_lifetime (__allocation.ptr , __allocation.count );
3422- auto __ptr = __get_long_pointer ();
3423- traits_type::copy (std::__to_address (__allocation.ptr ), std::__to_address (__ptr), __size + 1 );
3424- __alloc_traits::deallocate (__alloc_, __ptr, __get_long_cap ());
3425- __set_long_cap (__allocation.count );
3426- __set_long_pointer (__allocation.ptr );
3402+ auto __allocation = std::__allocate_at_least (__alloc_, __target_capacity + 1 );
3403+
3404+ // The Standard mandates shrink_to_fit() does not increase the capacity.
3405+ // With equal capacity keep the existing buffer. This avoids extra work
3406+ // due to swapping the elements.
3407+ if (__allocation.count - 1 > capacity ()) {
3408+ __alloc_traits::deallocate (__alloc_, __allocation.ptr , __allocation.count );
3409+ return ;
3410+ }
3411+ __new_data = __allocation.ptr ;
3412+ __target_capacity = __allocation.count - 1 ;
34273413#if _LIBCPP_HAS_EXCEPTIONS
3428- } catch (...) {
3429- return ;
3430- }
3414+ } catch (...) {
3415+ return ;
3416+ }
34313417#endif // _LIBCPP_HAS_EXCEPTIONS
3418+ }
3419+ __begin_lifetime (__new_data, __target_capacity + 1 );
3420+ __now_long = true ;
3421+ __was_long = __is_long ();
3422+ __p = __get_pointer ();
3423+ }
3424+ traits_type::copy (std::__to_address (__new_data), std::__to_address (__p), size () + 1 );
3425+ if (__was_long)
3426+ __alloc_traits::deallocate (__alloc_, __p, __cap + 1 );
3427+ if (__now_long) {
3428+ __set_long_cap (__target_capacity + 1 );
3429+ __set_long_size (__sz);
3430+ __set_long_pointer (__new_data);
3431+ } else
3432+ __set_short_size (__sz);
34323433}
34333434
34343435template <class _CharT , class _Traits , class _Allocator >
0 commit comments