@@ -2258,7 +2258,9 @@ private:
22582258 // Allocate a buffer of __capacity size with __alloc and return it
22592259 _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_SINCE_CXX20 __long
22602260 __allocate_long_buffer (_Allocator& __alloc, size_type __capacity) {
2261- auto __buffer = std::__allocate_at_least (__alloc, __recommend (__capacity) + 1 );
2261+ _LIBCPP_ASSERT_INTERNAL (!__fits_in_sso (__capacity),
2262+ " Trying to allocate long buffer for a capacity what would fit into the small buffer" );
2263+ auto __buffer = std::__allocate_at_least (__alloc, __align_allocation_size (__capacity));
22622264
22632265 if (__libcpp_is_constant_evaluated ()) {
22642266 for (size_type __i = 0 ; __i != __buffer.count ; ++__i)
@@ -2350,16 +2352,20 @@ private:
23502352 return (__s + (__a - 1 )) & ~(__a - 1 );
23512353 }
23522354 enum { __alignment = 8 };
2353- static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type __recommend (size_type __s) _NOEXCEPT {
2354- if (__s < __min_cap) {
2355- return static_cast <size_type>(__min_cap) - 1 ;
2356- }
2355+
2356+ // This makes sure that we're using a capacity with some extra alignment, since allocators almost always over-align
2357+ // the allocations anyways, improving memory usage. More importantly, this ensures that the lowest bit is never set
2358+ // if __endian_factor == 2, allowing us to store whether we're in the long string inside the lowest bit.
2359+ _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
2360+ __align_allocation_size (size_type __size) _NOEXCEPT {
2361+ _LIBCPP_ASSERT_INTERNAL (
2362+ !__fits_in_sso (__size), " Trying to align allocation of a size which would fit into the SSO" );
23572363 const size_type __boundary = sizeof (value_type) < __alignment ? __alignment / sizeof (value_type) : __endian_factor;
2358- size_type __guess = __align_it<__boundary>(__s + 1 ) - 1 ;
2359- if (__guess == __min_cap)
2364+ size_type __guess = __align_it<__boundary>(__size + 1 );
2365+ if (__guess == __min_cap + 1 )
23602366 __guess += __endian_factor;
23612367
2362- _LIBCPP_ASSERT_INTERNAL (__guess >= __s , " recommendation is below the requested size" );
2368+ _LIBCPP_ASSERT_INTERNAL (__guess >= __size , " aligned allocation size is below the requested size" );
23632369 return __guess;
23642370 }
23652371
@@ -2697,8 +2703,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::__
26972703 if (__delta_cap > __ms - __old_cap)
26982704 __throw_length_error ();
26992705 pointer __old_p = __get_pointer ();
2700- size_type __cap =
2701- __old_cap < __ms / 2 - __alignment ? __recommend (std::max (__old_cap + __delta_cap, 2 * __old_cap)) : __ms;
2706+ size_type __cap = __old_cap < __ms / 2 - __alignment ? std::max (__old_cap + __delta_cap, 2 * __old_cap) : __ms;
27022707 __annotate_delete ();
27032708 auto __guard = std::__make_scope_guard (__annotate_new_size (*this ));
27042709 __long __buffer = __allocate_long_buffer (__alloc_, __cap);
@@ -2735,8 +2740,7 @@ _LIBCPP_DEPRECATED_("use __grow_by_without_replace") basic_string<_CharT, _Trait
27352740 if (__delta_cap > __ms - __old_cap)
27362741 this ->__throw_length_error ();
27372742 pointer __old_p = __get_pointer ();
2738- size_type __cap =
2739- __old_cap < __ms / 2 - __alignment ? __recommend (std::max (__old_cap + __delta_cap, 2 * __old_cap)) : __ms;
2743+ size_type __cap = __old_cap < __ms / 2 - __alignment ? std::max (__old_cap + __delta_cap, 2 * __old_cap) : __ms;
27402744 __long __buffer = __allocate_long_buffer (__alloc_, __cap);
27412745 if (__n_copy != 0 )
27422746 traits_type::copy (std::__to_address (__buffer.__data_ ), std::__to_address (__old_p), __n_copy);
@@ -3402,25 +3406,25 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::re
34023406
34033407template <class _CharT , class _Traits , class _Allocator >
34043408inline _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::shrink_to_fit() _NOEXCEPT {
3405- size_type __target_capacity = __recommend (size ());
3406- if (__target_capacity == capacity ())
3409+ if (!__is_long ())
34073410 return ;
34083411
3409- _LIBCPP_ASSERT_INTERNAL (__is_long (), " Trying to shrink small string" );
3410-
3411- // We're a long string and we're shrinking into the small buffer.
34123412 const auto __ptr = __get_long_pointer ();
34133413 const auto __size = __get_long_size ();
34143414 const auto __cap = __get_long_cap ();
34153415
3416- if (__fits_in_sso (__target_capacity)) {
3416+ // We're a long string and we're shrinking into the small buffer.
3417+ if (__fits_in_sso (__size)) {
34173418 __annotation_guard __g (*this );
34183419 __set_short_size (__size);
34193420 traits_type::copy (std::__to_address (__get_short_pointer ()), std::__to_address (__ptr), __size + 1 );
34203421 __alloc_traits::deallocate (__alloc_, __ptr, __cap);
34213422 return ;
34223423 }
34233424
3425+ if (__align_allocation_size (__size) == __cap)
3426+ return ;
3427+
34243428# if _LIBCPP_HAS_EXCEPTIONS
34253429 try {
34263430# endif // _LIBCPP_HAS_EXCEPTIONS
0 commit comments