[RFC] libstdc++: Use allocate_at_least in vector, string (P0401) [PR118030]
Nathan Myers
ncm@cantrip.org
Sat Apr 4 04:52:35 GMT 2026
- Implement as much of allocator<>::allocate_at_least as is
possible relying solely on known alignment behavior of
standard operator new.
- Use allocator_at_least in string and vector to maximize usage
of actually allocated storage, so far as is known.
Defines and uses {string,vector}::_Alloc_result for all c++-*,
to reduce churn, actually calling allocate_at_least only where
defined. User-defined allocators can implement larger benefits,
and string and vector will take full advantage.
To do: Add tests; Fix constexpr test failures; By detecting
whether ::operator new has been replaced, we may rely on details
of our allocator implementation.
libstdc++-v3/ChangeLog:
PR libstdc++/118030
* include/bits/alloc_traits.h (allocate_at_least): Call
__a.allocate_at_least only if it exists.
* include/bits/allocator.h (allocate_at_least): Remove.
* include/bits/basic_string.h (_Alloc_result): Define.
(_S_allocate): Delete.
(_S_allocate_at_least): Define.
(assign): Use _S_allocate_at_least.
* include/bits/basic_string.tcc (_M_create, reserve, _M_replace):
Use _S_allocate_at_least.
* include/bits/memory_resource.h (allocate_at_least): Define.
* include/bits/new_allocator.h (allocate_at_least): Define.
* include/bits/stl_vector.h (_S_max_size): Move to _Vector_base.
(_M_allocate): Remove.
(_M_allocate_at_least): Define.
(_Alloc_result): Define.
(max_size, _S_check_init_len): Use _S_max_size as moved.
(_M_create_storage, append_range, _M_allocate_and_copy,
_M_initialize_dispatch, _M_range_initialize): Use _M_allocate_at_least.
(_M_check_len): Improve logic.
* include/bits/vector.tcc (reserve, _M_realloc_insert,
_M_realloc_append, _M_fill_insert, _M_fill_append, _M_default_append,
_M_range_insert, insert_range): Use _M_allocate_at_least.
* include/std/string: Define __glibcxx_want_allocate_at_least.
* include/std/vector: Same.
* testsuite/21_strings/basic_string/cons/wchar_t/constexpr.cc:
* testsuite/util/testsuite_allocator.h (allocate_at_least (2x)): Define.
(allocate): Use allocate_at_least.
---
libstdc++-v3/include/bits/alloc_traits.h | 9 +-
libstdc++-v3/include/bits/allocator.h | 6 --
libstdc++-v3/include/bits/basic_string.h | 24 +++--
libstdc++-v3/include/bits/basic_string.tcc | 17 ++--
libstdc++-v3/include/bits/memory_resource.h | 16 ++++
libstdc++-v3/include/bits/new_allocator.h | 43 +++++++++
libstdc++-v3/include/bits/stl_vector.h | 95 ++++++++++++-------
libstdc++-v3/include/bits/vector.tcc | 66 ++++++++-----
libstdc++-v3/include/std/string | 1 +
libstdc++-v3/include/std/vector | 1 +
.../basic_string/cons/wchar_t/constexpr.cc | 2 +-
.../testsuite/util/testsuite_allocator.h | 40 ++++++++
12 files changed, 239 insertions(+), 81 deletions(-)
diff --git a/libstdc++-v3/include/bits/alloc_traits.h b/libstdc++-v3/include/bits/alloc_traits.h
index 2be8ed561d4..b773be365bc 100644
--- a/libstdc++-v3/include/bits/alloc_traits.h
+++ b/libstdc++-v3/include/bits/alloc_traits.h
@@ -419,7 +419,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
*/
[[nodiscard]] static constexpr auto
allocate_at_least(_Alloc& __a, size_type __n)
- -> allocation_result<pointer, size_type>
+ -> allocation_result<pointer, size_type>
{
if constexpr (requires { __a.allocate_at_least(__n); })
return __a.allocate_at_least(__n);
@@ -672,7 +672,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
[[nodiscard]] static constexpr auto
allocate_at_least(allocator_type __a, size_type __n)
-> allocation_result<pointer, size_type>
- { return __a.allocate_at_least(__n); }
+ {
+ if constexpr(requires { __a.__allocate_at_least(__n); })
+ return __a.allocate_at_least(__n);
+ else
+ return { __a.allocate(__n), __n };
+ }
#endif
/**
diff --git a/libstdc++-v3/include/bits/allocator.h b/libstdc++-v3/include/bits/allocator.h
index 9c22c805ebe..ec160f44a1e 100644
--- a/libstdc++-v3/include/bits/allocator.h
+++ b/libstdc++-v3/include/bits/allocator.h
@@ -219,12 +219,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
#endif // C++20
-#ifdef __glibcxx_allocate_at_least // C++23
- [[nodiscard]] constexpr allocation_result<_Tp*, size_t>
- allocate_at_least(size_t __n)
- { return { this->allocate(__n), __n }; }
-#endif
-
friend __attribute__((__always_inline__)) _GLIBCXX20_CONSTEXPR
bool
operator==(const allocator&, const allocator&) _GLIBCXX_NOTHROW
diff --git a/libstdc++-v3/include/bits/basic_string.h b/libstdc++-v3/include/bits/basic_string.h
index 202a911f9ef..09a87d27910 100644
--- a/libstdc++-v3/include/bits/basic_string.h
+++ b/libstdc++-v3/include/bits/basic_string.h
@@ -135,10 +135,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
#endif
private:
- static _GLIBCXX20_CONSTEXPR pointer
- _S_allocate(_Char_alloc_type& __a, size_type __n)
+ struct _Alloc_result { pointer __ptr; size_type __count; };
+
+ static _GLIBCXX20_CONSTEXPR _Alloc_result
+ _S_allocate_at_least(_Char_alloc_type& __a, size_type __n)
{
- pointer __p = _Alloc_traits::allocate(__a, __n);
+ _Alloc_result __r;
+#ifdef __glibcxx_allocate_at_least // C++23
+ auto [__ptr, __count] = _Alloc_traits::allocate_at_least(__a, __n);
+ __r.__ptr = __ptr, __r.__count = __count;
+#else
+ __r.__ptr = _Alloc_traits::allocate(__a, __n), __r.__count = __n;
+#endif
#if __glibcxx_constexpr_string >= 201907L
// std::char_traits begins the lifetime of characters,
// but custom traits might not, so do it here.
@@ -146,9 +154,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
if (std::__is_constant_evaluated())
// Begin the lifetime of characters in allocated storage.
for (size_type __i = 0; __i < __n; ++__i)
- std::construct_at(__builtin_addressof(__p[__i]));
+ std::construct_at(__builtin_addressof(__r.__ptr[__i]));
#endif
- return __p;
+ return __r;
}
#ifdef __glibcxx_string_view // >= C++17
@@ -1782,10 +1790,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
const auto __len = __str.size();
auto __alloc = __str._M_get_allocator();
// If this allocation throws there are no effects:
- auto __ptr = _S_allocate(__alloc, __len + 1);
+ auto __r = _S_allocate_at_least(__alloc, __len + 1);
_M_destroy(_M_allocated_capacity);
- _M_data(__ptr);
- _M_capacity(__len);
+ _M_data(__r.__ptr);
+ _M_capacity(__r.__count);
_M_set_length(__len);
}
}
diff --git a/libstdc++-v3/include/bits/basic_string.tcc b/libstdc++-v3/include/bits/basic_string.tcc
index a223edf67ac..c56b214a3fd 100644
--- a/libstdc++-v3/include/bits/basic_string.tcc
+++ b/libstdc++-v3/include/bits/basic_string.tcc
@@ -161,7 +161,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// NB: Need an array of char_type[__capacity], plus a terminating
// null char_type() element.
- return _S_allocate(_M_get_allocator(), __capacity + 1);
+ _Alloc_result __r = _S_allocate_at_least(
+ _M_get_allocator(), __capacity + 1);
+ __capacity = __r.__count - 1;
+ return __r.__ptr;
}
// NB: This is the special case for Input Iterators, used in
@@ -444,11 +447,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
else if (__length < __capacity)
try
{
- pointer __tmp = _S_allocate(_M_get_allocator(), __length + 1);
- this->_S_copy(__tmp, _M_data(), __length + 1);
+ _Alloc_result __r = _S_allocate_at_least(
+ _M_get_allocator(), __length + 1);
+ this->_S_copy(__r.__ptr, _M_data(), __length + 1);
_M_dispose();
- _M_data(__tmp);
- _M_capacity(__length);
+ _M_data(__r.__ptr);
+ _M_capacity(__r.__count - 1); // reserve room for NUL.
}
catch (const __cxxabiv1::__forced_unwind&)
{ throw; }
@@ -588,7 +592,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#if __cpp_lib_is_constant_evaluated
if (std::is_constant_evaluated())
{
- auto __newp = _S_allocate(_M_get_allocator(), __new_size);
+ auto __newp =
+ _S_allocate_at_least(_M_get_allocator(), __new_size).__ptr;
_S_copy(__newp, this->_M_data(), __pos);
_S_copy(__newp + __pos, __s, __len2);
_S_copy(__newp + __pos + __len2, __p + __len1, __how_much);
diff --git a/libstdc++-v3/include/bits/memory_resource.h b/libstdc++-v3/include/bits/memory_resource.h
index e5c6697b07e..ee3872e2bcd 100644
--- a/libstdc++-v3/include/bits/memory_resource.h
+++ b/libstdc++-v3/include/bits/memory_resource.h
@@ -468,6 +468,22 @@ namespace pmr
allocate(allocator_type& __a, size_type __n, const_void_pointer)
{ return __a.allocate(__n); }
+#ifdef __glibcxx_allocate_at_least
+ /**
+ * @brief Allocate memory, generously.
+ * @param __a An allocator.
+ * @param __n The number of objects to allocate space for.
+ * @return Memory of suitable size and alignment for `n` objects
+ * of type `value_type`.
+ *
+ * Returns `a.allocate(n)`.
+ */
+ [[nodiscard]] static auto
+ allocate_at_least(allocator_type& __a, size_type __n)
+ -> std::allocation_result<pointer, size_type>
+ { return { __a.allocate(__n), __n }; }
+#endif
+
/**
* @brief Deallocate memory.
* @param __a An allocator.
diff --git a/libstdc++-v3/include/bits/new_allocator.h b/libstdc++-v3/include/bits/new_allocator.h
index fbe03e392aa..13018036e3a 100644
--- a/libstdc++-v3/include/bits/new_allocator.h
+++ b/libstdc++-v3/include/bits/new_allocator.h
@@ -162,6 +162,49 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return static_cast<_Tp*>(_GLIBCXX_OPERATOR_NEW(__n * sizeof(_Tp)));
}
+#ifdef __glibcxx_allocate_at_least // C++23
+ [[nodiscard]] constexpr auto
+ allocate_at_least(size_t __n)
+ -> std::allocation_result<_Tp*, size_t>
+ {
+ static_assert(requires { sizeof(_Tp); },
+ "cannot allocate incomplete types");
+
+ const std::size_t __align_mask = __STDCPP_DEFAULT_NEW_ALIGNMENT__ - 1;
+
+ if constexpr (!requires { sizeof(_Tp); })
+ return { nullptr, 0 }; // static_assert already failed
+ else if (__builtin_expect(__n > this->_M_max_size(), false))
+ {
+ if (__n > (std::size_t(-1) / sizeof(_Tp)))
+ std::__throw_bad_array_new_length();
+ std::__throw_bad_alloc();
+ }
+ else if constexpr (alignof(_Tp) > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
+ {
+ const std::align_val_t __al = std::align_val_t(alignof(_Tp));
+ auto __p = _GLIBCXX_OPERATOR_NEW(__n * sizeof(_Tp), __al);
+ return { static_cast<_Tp*>(__p), __n };
+ }
+ else if constexpr (sizeof(_Tp) > __align_mask)
+ {
+ auto __p = _GLIBCXX_OPERATOR_NEW(__n * sizeof(_Tp));
+ return { static_cast<_Tp*>(__p), __n };
+ }
+ else // See if more _Tp elements can fit into the allocation.
+ {
+ const auto __need = __n * sizeof(_Tp);
+ const auto __ask = (__need + __align_mask) & ~__align_mask;
+ const auto __p = _GLIBCXX_OPERATOR_NEW(__ask);
+ using _Uchar = const unsigned char;
+ static_assert(sizeof(_Tp) <= _Uchar(-1));
+ // Use 8-bit division.
+ _Uchar __spare = __ask - __need, __size = sizeof(_Tp);
+ return { static_cast<_Tp*>(__p), __n + __spare / __size };
+ }
+ }
+#endif
+
// __p is not permitted to be a null pointer.
_GLIBCXX20_CONSTEXPR void
deallocate(_Tp* __p, size_type __n __attribute__ ((__unused__)))
diff --git a/libstdc++-v3/include/bits/stl_vector.h b/libstdc++-v3/include/bits/stl_vector.h
index c4ca214752a..571018cb6f7 100644
--- a/libstdc++-v3/include/bits/stl_vector.h
+++ b/libstdc++-v3/include/bits/stl_vector.h
@@ -317,6 +317,19 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
get_allocator() const _GLIBCXX_NOEXCEPT
{ return allocator_type(_M_get_Tp_allocator()); }
+ static _GLIBCXX20_CONSTEXPR size_t
+ _S_max_size(const _Tp_alloc_type& __a) _GLIBCXX_NOEXCEPT
+ {
+ // std::distance(begin(), end()) cannot be greater than PTRDIFF_MAX,
+ // and realistically we can't store more than PTRDIFF_MAX/sizeof(T)
+ // (even if std::allocator_traits::max_size says we can).
+ const size_t __diffmax =
+ __gnu_cxx::__numeric_traits<ptrdiff_t>::__max / sizeof(_Tp);
+ const size_t __allocmax =
+ __gnu_cxx::__alloc_traits<_Alloc>::max_size(__a);
+ return (std::min)(__diffmax, __allocmax);
+ }
+
#if __cplusplus >= 201103L
_Vector_base() = default;
#else
@@ -381,12 +394,31 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
public:
_Vector_impl _M_impl;
+ struct _Alloc_result { pointer __ptr; size_t __count; };
+
_GLIBCXX20_CONSTEXPR
- pointer
- _M_allocate(size_t __n)
+ _Alloc_result
+ _M_allocate_at_least(size_t __n)
{
typedef __gnu_cxx::__alloc_traits<_Tp_alloc_type> _Tr;
- return __n != 0 ? _Tr::allocate(_M_impl, __n) : pointer();
+ _Alloc_result __r;
+ if (__builtin_expect(__n != 0, true))
+ {
+#ifdef __glibcxx_allocate_at_least // C++23
+ auto [__ptr, __count] = _Tr::allocate_at_least(_M_impl, __n);
+ if (__count > __n)
+ {
+ size_t __max = _S_max_size(_M_get_Tp_allocator());
+ if (__builtin_expect(__count > __max, false))
+ __count = __max;
+ }
+ __r.__ptr = __ptr, __r.__count = __count;
+#else
+ __r.__ptr = _Tr::allocate(_M_impl, __n), __r.__count = __n;
+#endif
+ }
+ else __r.__ptr = pointer(), __r.__count = 0;
+ return __r;
}
_GLIBCXX20_CONSTEXPR
@@ -404,9 +436,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
void
_M_create_storage(size_t __n)
{
- this->_M_impl._M_start = this->_M_allocate(__n);
- this->_M_impl._M_finish = this->_M_impl._M_start;
- this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n;
+ _Alloc_result __r = this->_M_allocate_at_least(__n);
+ this->_M_impl._M_finish = this->_M_impl._M_start = __r.__ptr;
+ this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __r.__count;
}
#if __glibcxx_containers_ranges // C++ >= 23
@@ -480,6 +512,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
typedef _Vector_base<_Tp, _Alloc> _Base;
typedef typename _Base::_Tp_alloc_type _Tp_alloc_type;
typedef __gnu_cxx::__alloc_traits<_Tp_alloc_type> _Alloc_traits;
+ typedef typename _Base::_Alloc_result _Alloc_result;
public:
typedef _Tp value_type;
@@ -535,7 +568,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
#endif // C++11
protected:
- using _Base::_M_allocate;
+ using _Base::_M_allocate_at_least;
using _Base::_M_deallocate;
using _Base::_M_impl;
using _Base::_M_get_Tp_allocator;
@@ -1116,7 +1149,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
_GLIBCXX_NODISCARD _GLIBCXX20_CONSTEXPR
size_type
max_size() const _GLIBCXX_NOEXCEPT
- { return _S_max_size(_M_get_Tp_allocator()); }
+ { return _Base::_S_max_size(_M_get_Tp_allocator()); }
#if __cplusplus >= 201103L
/**
@@ -1682,16 +1715,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
return;
}
- const size_type __len = _M_check_len(__n, "vector::append_range");
+ const size_type __len1 = _M_check_len(__n, "vector::append_range");
pointer __old_start = this->_M_impl._M_start;
pointer __old_finish = this->_M_impl._M_finish;
- allocator_type& __a = _M_get_Tp_allocator();
- const pointer __start = this->_M_allocate(__len);
+ auto [__ptr, __count] = this->_M_allocate_at_least(__len1);
+ const size_type __len = __count;
+ const pointer __start = __ptr;
const pointer __mid = __start + __sz;
const pointer __back = __mid + __n;
_Guard_alloc __guard(__start, __len, *this);
+ allocator_type& __a = _M_get_Tp_allocator();
std::__uninitialized_copy_a(ranges::begin(__rg),
ranges::end(__rg),
__mid, __a);
@@ -1897,7 +1932,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
_M_allocate_and_copy(size_type __n,
_ForwardIterator __first, _ForwardIterator __last)
{
- _Guard_alloc __guard(this->_M_allocate(__n), __n, *this);
+ _Alloc_result __r = this->_M_allocate_at_least(__n);
+ _Guard_alloc __guard(__r.__ptr, __r.__count, *this);
std::__uninitialized_copy_a
(__first, __last, __guard._M_storage, _M_get_Tp_allocator());
return __guard._M_release();
@@ -1916,8 +1952,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
_M_initialize_dispatch(_Integer __int_n, _Integer __value, __true_type)
{
const size_type __n = static_cast<size_type>(__int_n);
- pointer __start =
- _M_allocate(_S_check_init_len(__n, _M_get_Tp_allocator()));
+ pointer __start = _M_allocate_at_least(
+ _S_check_init_len(__n, _M_get_Tp_allocator())).__ptr;
this->_M_impl._M_start = __start;
this->_M_impl._M_end_of_storage = __start + __n;
_M_fill_initialize(__n, __value);
@@ -1971,10 +2007,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
_M_range_initialize_n(_Iterator __first, _Sentinel __last,
size_type __n)
{
- pointer __start =
- this->_M_allocate(_S_check_init_len(__n, _M_get_Tp_allocator()));
+ _Alloc_result __r = this->_M_allocate_at_least(
+ _S_check_init_len(__n, _M_get_Tp_allocator()));
+ pointer __start = __r.__ptr;
this->_M_impl._M_start = this->_M_impl._M_finish = __start;
- this->_M_impl._M_end_of_storage = __start + __n;
+ this->_M_impl._M_end_of_storage = __start + __r.__count;
this->_M_impl._M_finish
= std::__uninitialized_copy_a(_GLIBCXX_MOVE(__first), __last,
__start, _M_get_Tp_allocator());
@@ -2191,35 +2228,27 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
size_type
_M_check_len(size_type __n, const char* __s) const
{
- if (max_size() - size() < __n)
+ const size_type __room = max_size() - size();
+ if (__room < __n)
__throw_length_error(__N(__s));
- const size_type __len = size() + (std::max)(size(), __n);
- return (__len < size() || __len > max_size()) ? max_size() : __len;
+ if (__n < size())
+ __n = size(); // Grow by (at least) doubling ...
+ if (__n > __room)
+ __n = __room; // ... but only as much as will fit.
+ return size() + __n;
}
// Called by constructors to check initial size.
static _GLIBCXX20_CONSTEXPR size_type
_S_check_init_len(size_type __n, const allocator_type& __a)
{
- if (__n > _S_max_size(_Tp_alloc_type(__a)))
+ if (__n > _Base::_S_max_size(_Tp_alloc_type(__a)))
__throw_length_error(
__N("cannot create std::vector larger than max_size()"));
return __n;
}
- static _GLIBCXX20_CONSTEXPR size_type
- _S_max_size(const _Tp_alloc_type& __a) _GLIBCXX_NOEXCEPT
- {
- // std::distance(begin(), end()) cannot be greater than PTRDIFF_MAX,
- // and realistically we can't store more than PTRDIFF_MAX/sizeof(T)
- // (even if std::allocator_traits::max_size says we can).
- const size_t __diffmax
- = __gnu_cxx::__numeric_traits<ptrdiff_t>::__max / sizeof(_Tp);
- const size_t __allocmax = _Alloc_traits::max_size(__a);
- return (std::min)(__diffmax, __allocmax);
- }
-
// Internal erase functions follow.
// Called by erase(q1,q2), clear(), resize(), _M_fill_assign,
diff --git a/libstdc++-v3/include/bits/vector.tcc b/libstdc++-v3/include/bits/vector.tcc
index b790fca2964..9524038b7d1 100644
--- a/libstdc++-v3/include/bits/vector.tcc
+++ b/libstdc++-v3/include/bits/vector.tcc
@@ -79,7 +79,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
#if __cplusplus >= 201103L
if constexpr (_S_use_relocate())
{
- __tmp = this->_M_allocate(__n);
+ auto __res = this->_M_allocate_at_least(__n);
+ __tmp = __res.__ptr;
+ __n = __res.__count;
std::__relocate_a(this->_M_impl._M_start, this->_M_impl._M_finish,
__tmp, _M_get_Tp_allocator());
}
@@ -106,12 +108,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
#if __cplusplus >= 201103L
template<typename _Tp, typename _Alloc>
template<typename... _Args>
-#if __cplusplus > 201402L
+# if __cplusplus > 201402L
_GLIBCXX20_CONSTEXPR
typename vector<_Tp, _Alloc>::reference
-#else
+# else
void
-#endif
+# endif
vector<_Tp, _Alloc>::
emplace_back(_Args&&... __args)
{
@@ -125,11 +127,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
}
else
_M_realloc_append(std::forward<_Args>(__args)...);
-#if __cplusplus > 201402L
+# if __cplusplus > 201402L
return back();
-#endif
+# endif
}
-#endif
+#endif // __cplusplus >= 201103L
template<typename _Tp, typename _Alloc>
_GLIBCXX20_CONSTEXPR
@@ -464,13 +466,15 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
_M_realloc_insert(iterator __position, const _Tp& __x)
#endif
{
- const size_type __len = _M_check_len(1u, "vector::_M_realloc_insert");
- if (__len <= 0)
+ const size_type __len1 = _M_check_len(1u, "vector::_M_realloc_insert");
+ if (__len1 <= 0)
__builtin_unreachable();
pointer __old_start = this->_M_impl._M_start;
pointer __old_finish = this->_M_impl._M_finish;
const size_type __elems_before = __position - begin();
- pointer __new_start(this->_M_allocate(__len));
+ _Alloc_result __r = this->_M_allocate_at_least(__len1);
+ const size_type __len = __r.__count;
+ pointer __new_start(__r.__ptr);
pointer __new_finish(__new_start);
{
@@ -574,14 +578,16 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
const size_type __len = _M_check_len(1u, "vector::_M_realloc_append");
if (__len <= 0)
__builtin_unreachable();
- pointer __old_start = this->_M_impl._M_start;
- pointer __old_finish = this->_M_impl._M_finish;
+ const pointer __old_start = this->_M_impl._M_start;
+ const pointer __old_finish = this->_M_impl._M_finish;
const size_type __elems = size();
- pointer __new_start(this->_M_allocate(__len));
+ const _Alloc_result __r = this->_M_allocate_at_least(__len);
+ const size_type __rlen = __r.__count;
+ const pointer __new_start(__r.__ptr);
pointer __new_finish(__new_start);
{
- _Guard_alloc __guard(__new_start, __len, *this);
+ _Guard_alloc __guard(__new_start, __rlen, *this);
// The order of the three operations is dictated by the C++11
// case, where the moves could alter a new element belonging
@@ -652,7 +658,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
this->_M_impl._M_start = __new_start;
this->_M_impl._M_finish = __new_finish;
- this->_M_impl._M_end_of_storage = __new_start + __len;
+ this->_M_impl._M_end_of_storage = __new_start + __rlen;
}
#pragma GCC diagnostic pop
@@ -716,10 +722,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
pointer __old_finish = this->_M_impl._M_finish;
const pointer __pos = __position.base();
- const size_type __len =
+ const size_type __len1 =
_M_check_len(__n, "vector::_M_fill_insert");
const size_type __elems_before = __pos - __old_start;
- pointer __new_start(this->_M_allocate(__len));
+ _Alloc_result __r = this->_M_allocate_at_least(__len1);
+ const size_type __len = __r.__count;
+ pointer __new_start(__r.__ptr);
pointer __new_finish(__new_start);
__try
{
@@ -787,7 +795,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
const size_type __len =
_M_check_len(__n, "vector::_M_fill_append");
- pointer __new_start(this->_M_allocate(__len));
+ _Alloc_result __r = this->_M_allocate_at_least(__len);
+ __len = __r.__count;
+ pointer __new_start(__r.__ptr);
pointer __new_finish(__new_start + __old_size);
__try
{
@@ -852,9 +862,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
pointer __old_start = this->_M_impl._M_start;
pointer __old_finish = this->_M_impl._M_finish;
- const size_type __len =
+ const size_type __len1 =
_M_check_len(__n, "vector::_M_default_append");
- pointer __new_start(this->_M_allocate(__len));
+ _Alloc_result __r = this->_M_allocate_at_least(__len1);
+ const size_type __len = __r.__count;
+ pointer __new_start(__r.__ptr);
{
_Guard_alloc __guard(__new_start, __len, *this);
@@ -1003,14 +1015,16 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
pointer __old_start = this->_M_impl._M_start;
pointer __old_finish = this->_M_impl._M_finish;
- const size_type __len =
+ const size_type __len1 =
_M_check_len(__n, "vector::_M_range_insert");
#if __cplusplus < 201103L
- if (__len < (__n + (__old_finish - __old_start)))
+ if (__len1 < (__n + (__old_finish - __old_start)))
__builtin_unreachable();
#endif
- pointer __new_start(this->_M_allocate(__len));
+ _Alloc_result __r = this->_M_allocate_at_least(__len1);
+ const size_type __len = __r.__count;
+ pointer __new_start(__r.__ptr);
pointer __new_finish(__new_start);
__try
{
@@ -1111,7 +1125,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
}
else // Reallocate
{
- const size_type __len
+ const size_type __len1
= _M_check_len(__n, "vector::insert_range");
struct _Guard : _Guard_alloc
@@ -1130,7 +1144,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
};
// Allocate new storage:
- pointer __new_start(this->_M_allocate(__len));
+ _Alloc_result __r = this->_M_allocate_at_least(__len1);
+ const size_type __len = __r.__count;
+ pointer __new_start(__r.__ptr);
_Guard __guard(__new_start, __len, *this);
auto& __alloc = _M_get_Tp_allocator();
diff --git a/libstdc++-v3/include/std/string b/libstdc++-v3/include/std/string
index c2b37391fc7..da50bce2e7d 100644
--- a/libstdc++-v3/include/std/string
+++ b/libstdc++-v3/include/std/string
@@ -67,6 +67,7 @@
#endif
#define __glibcxx_want_algorithm_default_value_type
+#define __glibcxx_want_allocate_at_least
#define __glibcxx_want_allocator_traits_is_always_equal
#define __glibcxx_want_constexpr_char_traits
#define __glibcxx_want_constexpr_string
diff --git a/libstdc++-v3/include/std/vector b/libstdc++-v3/include/std/vector
index 343483e9519..ac041cc9408 100644
--- a/libstdc++-v3/include/std/vector
+++ b/libstdc++-v3/include/std/vector
@@ -79,6 +79,7 @@
#endif
#define __glibcxx_want_algorithm_default_value_type
+#define __glibcxx_want_allocate_at_least
#define __glibcxx_want_allocator_traits_is_always_equal
#define __glibcxx_want_constexpr_vector
#define __glibcxx_want_containers_ranges
diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/cons/wchar_t/constexpr.cc b/libstdc++-v3/testsuite/21_strings/basic_string/cons/wchar_t/constexpr.cc
index 44c8391ebc2..9942e1cfb6a 100644
--- a/libstdc++-v3/testsuite/21_strings/basic_string/cons/wchar_t/constexpr.cc
+++ b/libstdc++-v3/testsuite/21_strings/basic_string/cons/wchar_t/constexpr.cc
@@ -34,7 +34,7 @@ struct Alloc : std::allocator<T>
{ return personality == a.personality; }
};
-constexpr bool
+consteexpr bool
test_default_ctor()
{
std::basic_string<C> s0;
diff --git a/libstdc++-v3/testsuite/util/testsuite_allocator.h b/libstdc++-v3/testsuite/util/testsuite_allocator.h
index 892a385e307..d8914229c7a 100644
--- a/libstdc++-v3/testsuite/util/testsuite_allocator.h
+++ b/libstdc++-v3/testsuite/util/testsuite_allocator.h
@@ -176,6 +176,16 @@ namespace __gnu_test
return p;
}
+#ifdef __glibcxx_allocate_at_least
+ std::allocation_result<pointer, size_type>
+ allocate_at_least(size_type n)
+ {
+ auto r = AllocTraits::allocate_at_least(*this, n);
+ counter_type::allocate(r.count * sizeof(T));
+ return r;
+ }
+#endif
+
#if __cplusplus >= 201103L
template<typename U, typename... Args>
void
@@ -353,6 +363,9 @@ namespace __gnu_test
_GLIBCXX20_CONSTEXPR
pointer
allocate(size_type n, const void* = 0)
+#ifdef __glibcxx_allocate_at_least
+ { return this->allocate_at_least(n).ptr; }
+#else
{
pointer p = AllocTraits::allocate(*this, n);
@@ -372,6 +385,33 @@ namespace __gnu_test
return p;
}
+#endif
+
+#ifdef __glibcxx_allocate_at_least
+ _GLIBCXX20_CONSTEXPR
+ auto
+ allocate_at_least(size_type n)
+ -> std::allocation_result<Tp*, size_t>
+ {
+ auto r = AllocTraits::allocate_at_least(*this, n);
+
+ if (std::__is_constant_evaluated())
+ return r;
+
+ try
+ {
+ get_map().insert(map_type::value_type(
+ reinterpret_cast<void*>(r.ptr), personality));
+ }
+ catch(...)
+ {
+ AllocTraits::deallocate(*this, r.ptr, r.count);
+ __throw_exception_again;
+ }
+
+ return r;
+ }
+#endif
_GLIBCXX14_CONSTEXPR
void
--
2.53.0
More information about the Libstdc++
mailing list