diff --git a/libcxx/include/__config b/libcxx/include/__config --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -113,6 +113,8 @@ # define _LIBCPP_ABI_ENABLE_UNIQUE_PTR_TRIVIAL_ABI // Enable clang::trivial_abi on std::shared_ptr and std::weak_ptr # define _LIBCPP_ABI_ENABLE_SHARED_PTR_TRIVIAL_ABI +// Use exact string sizes on `resize()` rather than exponential growth. +# define _LIBCPP_ABI_EXACT_STRING_RESIZE #elif _LIBCPP_ABI_VERSION == 1 # if !defined(_LIBCPP_OBJECT_FORMAT_COFF) // Enable compiling copies of now inline methods into the dylib to support diff --git a/libcxx/include/string b/libcxx/include/string --- a/libcxx/include/string +++ b/libcxx/include/string @@ -3276,9 +3276,13 @@ basic_string<_CharT, _Traits, _Allocator>::resize(size_type __n, value_type __c) { size_type __sz = size(); - if (__n > __sz) + if (__n > __sz) { +#ifdef _LIBCPP_ABI_EXACT_STRING_RESIZE + if (__n > capacity()) + __shrink_or_extend(__recommend(__n)); +#endif append(__n - __sz, __c); - else + } else __erase_to_end(__n); } @@ -3288,7 +3292,11 @@ { size_type __sz = size(); if (__n > __sz) { - __append_default_init(__n - __sz); +#ifdef _LIBCPP_ABI_EXACT_STRING_RESIZE + if (__n > capacity()) + __shrink_or_extend(__recommend(__n)); +#endif + __append_default_init(__n - __sz); } else __erase_to_end(__n); } diff --git a/libcxx/test/libcxx/strings/basic.string/string.capacity/resize.exact.cpp b/libcxx/test/libcxx/strings/basic.string/string.capacity/resize.exact.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/strings/basic.string/string.capacity/resize.exact.cpp @@ -0,0 +1,66 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// void resize(size_type); + +// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ABI_EXACT_STRING_RESIZE + +#include +#include +#include + +#include "test_macros.h" +#include "min_allocator.h" + +bool is_between(size_t c, size_t low, size_t hi) { return c >= low && c <= hi; } + +template +void test() { + // Tests that resize() uses exact growth rather than exponential growth. + const size_t kSsoSize = S().capacity(); + // basic_string uses buffers whose lengths are multiples of this alignment. + const size_t kAlign = 16; + for (int i = kSsoSize + 1; i < 1024; ++i) { + S s; + s.resize(i); + assert(s.size() == i); + assert(is_between(s.capacity(), i, i + kAlign)); + } + for (int i = kSsoSize + 1; i < 1024; ++i) { + S s; + for (int j = 0; j < i; ++j) { + s.push_back(typename S::value_type()); + } + assert(s.size() == i); + const size_t prev_cap = s.capacity(); + s.resize(prev_cap + 10); + assert(is_between(s.capacity(), prev_cap + 10, prev_cap + 10 + kAlign)); + } +} + +int main(int, char**) { + { + typedef std::string S; + test(); + } + { + typedef std::wstring S; + test(); + } +#if TEST_STD_VER >= 11 + { + typedef min_allocator A; + typedef std::basic_string, A> S; + test(); + } +#endif + + return 0; +}