[RFC] libstdc++: Use allocate_at_least in vector, string (P0401) [PR118030]
Checks
Commit Message
- 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(-)
Comments
On Sat, 4 Apr 2026, 06:03 Nathan Myers, <ncm@cantrip.org> wrote:
> - 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
>
The standard requires this to exist, so if you move it to the base class
you need to implement it in all the possible base classes:
malloc_allocator, bitmap_allocator, etc.
Alternatively, keep it here, but use a requires expression to see if it's
present in the base class, and either call base::allocate_at_least or
base::allocate.
-
> 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)
>
Removing this function will break ABI.
I think it needs to be kept, even if it is no longer used.
+ 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]));
>
Do we need to begin the lifetime of all characters up to r.count, not only
up to n?
#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;
>
Doesn't this just discard any additional capacity that was allocated?
_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
>
Typo
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);
>
Is this recursive now?
+ 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
>
>
@@ -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
/**
@@ -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
@@ -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);
}
}
@@ -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);
@@ -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.
@@ -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__)))
@@ -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,
@@ -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();
@@ -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
@@ -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
@@ -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;
@@ -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