Skip to content

Commit e95c5c8

Browse files
authored
[libc++] Refactor basic_string::__recommend (#162631)
This does a couple of things: - code that is only useful for `shrink_to_fit` is moved into that function - `shrink_to_fit` is simplified a bit - `__recommend` is renamed to better reflect what the function actually does - `__allocate_long_buffer` asserts that the passed capacity doesn't fit into the SSO
1 parent 53e3f8e commit e95c5c8

File tree

1 file changed

+22
-18
lines changed

1 file changed

+22
-18
lines changed

libcxx/include/string

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -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

34183422
template <class _CharT, class _Traits, class _Allocator>
34193423
inline _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

Comments
 (0)