https://github.com/llvmbot created https://github.com/llvm/llvm-project/pull/100149
Backport d0ca9f2 Requested by: @ldionne >From c264db19ef5ac5de02596ebb8ff3774394c871b4 Mon Sep 17 00:00:00 2001 From: Mark de Wever <ko...@xs4all.nl> Date: Tue, 23 Jul 2024 18:13:22 +0200 Subject: [PATCH] [libc++][string] Fixes shrink_to_fit. (#97961) This ensures that shrink_to_fit does not increase the allocated size. Partly addresses #95161 (cherry picked from commit d0ca9f23e8f25b0509c3ff34ed215508b39ea6e7) --- libcxx/include/string | 17 ++++++-- .../string.capacity/shrink_to_fit.pass.cpp | 41 +++++++++++++++++++ 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/libcxx/include/string b/libcxx/include/string index ba86a32090825..9fa979e3a5178 100644 --- a/libcxx/include/string +++ b/libcxx/include/string @@ -3358,23 +3358,34 @@ basic_string<_CharT, _Traits, _Allocator>::__shrink_or_extend(size_type __target __p = __get_long_pointer(); } else { if (__target_capacity > __cap) { + // Extend + // - called from reserve should propagate the exception thrown. auto __allocation = std::__allocate_at_least(__alloc(), __target_capacity + 1); __new_data = __allocation.ptr; __target_capacity = __allocation.count - 1; } else { + // Shrink + // - called from shrink_to_fit should not throw. + // - called from reserve may throw but is not required to. #ifndef _LIBCPP_HAS_NO_EXCEPTIONS try { #endif // _LIBCPP_HAS_NO_EXCEPTIONS auto __allocation = std::__allocate_at_least(__alloc(), __target_capacity + 1); + + // The Standard mandates shrink_to_fit() does not increase the capacity. + // With equal capacity keep the existing buffer. This avoids extra work + // due to swapping the elements. + if (__allocation.count - 1 > __target_capacity) { + __alloc_traits::deallocate(__alloc(), __allocation.ptr, __allocation.count); + __annotate_new(__sz); // Undoes the __annotate_delete() + return; + } __new_data = __allocation.ptr; __target_capacity = __allocation.count - 1; #ifndef _LIBCPP_HAS_NO_EXCEPTIONS } catch (...) { return; } -#else // _LIBCPP_HAS_NO_EXCEPTIONS - if (__new_data == nullptr) - return; #endif // _LIBCPP_HAS_NO_EXCEPTIONS } __begin_lifetime(__new_data, __target_capacity + 1); diff --git a/libcxx/test/std/strings/basic.string/string.capacity/shrink_to_fit.pass.cpp b/libcxx/test/std/strings/basic.string/string.capacity/shrink_to_fit.pass.cpp index 057050cdcf7fa..6f5e43d1341f5 100644 --- a/libcxx/test/std/strings/basic.string/string.capacity/shrink_to_fit.pass.cpp +++ b/libcxx/test/std/strings/basic.string/string.capacity/shrink_to_fit.pass.cpp @@ -63,8 +63,49 @@ TEST_CONSTEXPR_CXX20 bool test() { return true; } +#if TEST_STD_VER >= 23 +std::size_t min_bytes = 1000; + +template <typename T> +struct increasing_allocator { + using value_type = T; + increasing_allocator() = default; + template <typename U> + increasing_allocator(const increasing_allocator<U>&) noexcept {} + std::allocation_result<T*> allocate_at_least(std::size_t n) { + std::size_t allocation_amount = n * sizeof(T); + if (allocation_amount < min_bytes) + allocation_amount = min_bytes; + min_bytes += 1000; + return {static_cast<T*>(::operator new(allocation_amount)), allocation_amount / sizeof(T)}; + } + T* allocate(std::size_t n) { return allocate_at_least(n).ptr; } + void deallocate(T* p, std::size_t) noexcept { ::operator delete(static_cast<void*>(p)); } +}; + +template <typename T, typename U> +bool operator==(increasing_allocator<T>, increasing_allocator<U>) { + return true; +} + +// https://github.com/llvm/llvm-project/issues/95161 +void test_increasing_allocator() { + std::basic_string<char, std::char_traits<char>, increasing_allocator<char>> s{ + "String does not fit in the internal buffer"}; + std::size_t capacity = s.capacity(); + std::size_t size = s.size(); + s.shrink_to_fit(); + assert(s.capacity() <= capacity); + assert(s.size() == size); + LIBCPP_ASSERT(is_string_asan_correct(s)); +} +#endif // TEST_STD_VER >= 23 + int main(int, char**) { test(); +#if TEST_STD_VER >= 23 + test_increasing_allocator(); +#endif #if TEST_STD_VER > 17 static_assert(test()); #endif _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits