@@ -2273,7 +2273,9 @@ private:
22732273 // Allocate a buffer of __capacity size with __alloc and return it
22742274 _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_SINCE_CXX20 __long
22752275 __allocate_long_buffer (_Allocator& __alloc, size_type __capacity) {
2276- auto __buffer = std::__allocate_at_least (__alloc, __recommend (__capacity) + 1 );
2276+ _LIBCPP_ASSERT_INTERNAL (!__fits_in_sso (__capacity),
2277+ " Trying to allocate long buffer for a capacity what would fit into the small buffer" );
2278+ auto __buffer = std::__allocate_at_least (__alloc, __align_allocation_size (__capacity));
22772279
22782280 if (__libcpp_is_constant_evaluated ()) {
22792281 for (size_type __i = 0 ; __i != __buffer.count ; ++__i)
@@ -2365,16 +2367,20 @@ private:
23652367 return (__s + (__a - 1 )) & ~(__a - 1 );
23662368 }
23672369 enum { __alignment = 8 };
2368- static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type __recommend (size_type __s) _NOEXCEPT {
2369- if (__s < __min_cap) {
2370- return static_cast <size_type>(__min_cap) - 1 ;
2371- }
2370+
2371+ // This makes sure that we're using a capacity with some extra alignment, since allocators almost always over-align
2372+ // the allocations anyways, improving memory usage. More importantly, this ensures that the lowest bit is never set
2373+ // if __endian_factor == 2, allowing us to store whether we're in the long string inside the lowest bit.
2374+ _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
2375+ __align_allocation_size (size_type __size) _NOEXCEPT {
2376+ _LIBCPP_ASSERT_INTERNAL (
2377+ !__fits_in_sso (__size), " Trying to align allocation of a size which would fit into the SSO" );
23722378 const size_type __boundary = sizeof (value_type) < __alignment ? __alignment / sizeof (value_type) : __endian_factor;
2373- size_type __guess = __align_it<__boundary>(__s + 1 ) - 1 ;
2374- if (__guess == __min_cap)
2379+ size_type __guess = __align_it<__boundary>(__size + 1 );
2380+ if (__guess == __min_cap + 1 )
23752381 __guess += __endian_factor;
23762382
2377- _LIBCPP_ASSERT_INTERNAL (__guess >= __s , " recommendation is below the requested size" );
2383+ _LIBCPP_ASSERT_INTERNAL (__guess >= __size , " aligned allocation size is below the requested size" );
23782384 return __guess;
23792385 }
23802386
@@ -2712,8 +2718,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::__
27122718 if (__delta_cap > __ms - __old_cap)
27132719 __throw_length_error ();
27142720 pointer __old_p = __get_pointer ();
2715- size_type __cap =
2716- __old_cap < __ms / 2 - __alignment ? __recommend (std::max (__old_cap + __delta_cap, 2 * __old_cap)) : __ms;
2721+ size_type __cap = __old_cap < __ms / 2 - __alignment ? std::max (__old_cap + __delta_cap, 2 * __old_cap) : __ms;
27172722 __annotate_delete ();
27182723 auto __guard = std::__make_scope_guard (__annotate_new_size (*this ));
27192724 __long __buffer = __allocate_long_buffer (__alloc_, __cap);
@@ -2750,8 +2755,7 @@ _LIBCPP_DEPRECATED_("use __grow_by_without_replace") basic_string<_CharT, _Trait
27502755 if (__delta_cap > __ms - __old_cap)
27512756 this ->__throw_length_error ();
27522757 pointer __old_p = __get_pointer ();
2753- size_type __cap =
2754- __old_cap < __ms / 2 - __alignment ? __recommend (std::max (__old_cap + __delta_cap, 2 * __old_cap)) : __ms;
2758+ size_type __cap = __old_cap < __ms / 2 - __alignment ? std::max (__old_cap + __delta_cap, 2 * __old_cap) : __ms;
27552759 __long __buffer = __allocate_long_buffer (__alloc_, __cap);
27562760 if (__n_copy != 0 )
27572761 traits_type::copy (std::__to_address (__buffer.__data_ ), std::__to_address (__old_p), __n_copy);
@@ -3417,25 +3421,25 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::re
34173421
34183422template <class _CharT , class _Traits , class _Allocator >
34193423inline _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::shrink_to_fit() _NOEXCEPT {
3420- size_type __target_capacity = __recommend (size ());
3421- if (__target_capacity == capacity ())
3424+ if (!__is_long ())
34223425 return ;
34233426
3424- _LIBCPP_ASSERT_INTERNAL (__is_long (), " Trying to shrink small string" );
3425-
3426- // We're a long string and we're shrinking into the small buffer.
34273427 const auto __ptr = __get_long_pointer ();
34283428 const auto __size = __get_long_size ();
34293429 const auto __cap = __get_long_cap ();
34303430
3431- if (__fits_in_sso (__target_capacity)) {
3431+ // We're a long string and we're shrinking into the small buffer.
3432+ if (__fits_in_sso (__size)) {
34323433 __annotation_guard __g (*this );
34333434 __set_short_size (__size);
34343435 traits_type::copy (std::__to_address (__get_short_pointer ()), std::__to_address (__ptr), __size + 1 );
34353436 __alloc_traits::deallocate (__alloc_, __ptr, __cap);
34363437 return ;
34373438 }
34383439
3440+ if (__align_allocation_size (__size) == __cap)
3441+ return ;
3442+
34393443# if _LIBCPP_HAS_EXCEPTIONS
34403444 try {
34413445# endif // _LIBCPP_HAS_EXCEPTIONS
0 commit comments