Skip to content

Commit f3e4a9a

Browse files
committed
[libc++][NFC] Simplify the implementation of reserve() and shrink_to_fit()
1 parent aaa0dd2 commit f3e4a9a

File tree

1 file changed

+59
-60
lines changed

1 file changed

+59
-60
lines changed

libcxx/include/string

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

33643386
template <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

34353434
template <class _CharT, class _Traits, class _Allocator>

0 commit comments

Comments
 (0)