@@ -1884,8 +1884,6 @@ 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-
18891887 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS bool
18901888 __is_long () const _NOEXCEPT {
18911889 if (__libcpp_is_constant_evaluated () && __builtin_constant_p (__rep_.__l .__is_long_ )) {
@@ -2080,6 +2078,21 @@ private:
20802078#endif
20812079 }
20822080
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+
20832096 template <size_type __a>
20842097 static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type __align_it (size_type __s) _NOEXCEPT {
20852098 return (__s + (__a - 1 )) & ~(__a - 1 );
@@ -3358,7 +3371,16 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::re
33583371 if (__requested_capacity <= capacity ())
33593372 return ;
33603373
3361- __shrink_or_extend (__recommend (__requested_capacity));
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 );
33623384}
33633385
33643386template <class _CharT , class _Traits , class _Allocator >
@@ -3367,69 +3389,46 @@ inline _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocat
33673389 if (__target_capacity == capacity ())
33683390 return ;
33693391
3370- __shrink_or_extend (__target_capacity);
3371- }
3372-
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 ();
3392+ _LIBCPP_ASSERT_INTERNAL (__is_long (), " Trying to shrink small string" );
33803393
3381- pointer __new_data, __p;
3382- bool __was_long, __now_long;
3394+ // We're a long string and we're shrinking into the small buffer.
33833395 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.
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+ }
3405+
33993406#if _LIBCPP_HAS_EXCEPTIONS
3400- try {
3407+ try {
34013408#endif // _LIBCPP_HAS_EXCEPTIONS
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 ;
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 );
34133427#if _LIBCPP_HAS_EXCEPTIONS
3414- } catch (...) {
3415- return ;
3416- }
3428+ } catch (...) {
3429+ return ;
3430+ }
34173431#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);
34333432}
34343433
34353434template <class _CharT , class _Traits , class _Allocator >
0 commit comments