[PATCH] libstdc++: Use allocate_at_least in vector, string (P0401) [PR118030]
Nathan Myers
ncm@cantrip.org
Tue Apr 21 17:35:42 GMT 2026
Thank you.
On 4/20/26 10:16 AM, Jonathan Wakely wrote:
> On Sat, 18 Apr 2026 at 17:56 -0400, Nathan Myers wrote:
>> Changes in v2:
>> - Avoid PR108377 "exceeds maximum object size" warning
>> differently, contained to __allocate_to_alignment.
>> - Rename _S_allocate_to_alignment to __allocate_to_alignment
>> because it is not a member. Generalize it for arbitrary pointer
>> and size types.
>> - Declare allocator_traits<allocator<void>>::allocate_at_least
>> with " = delete;", matching others.
>> - In allocator_traits<>::allocate_at_least, de-structure and
>> reconstruct the allocation result to enable an implicit
>> pointer type conversion on the way, as used (e.g.) for
>> testsuite_allocator.h.
>> - Define allocate_at_least in the polymorphic specialization
>> of allocator_traits (bits/memory_resource.h), and in the
>> testsuite allocator, destructuring likewise.
>> - In string:
>> - Make _M_create use _M_allocate_at_least. (This function
>> is no longer used, but is retained for ABI stability.)
>> - Define (in addition to _M_create_plus) a new allocation
>> helper function _M_create_and_place to abstract common
>> operations, and use it where suitable.
>> - Add new member signatures to config/abi/pre/gnu.ver.
>> - Relax shrink_to_fit()/reserve() to allow a little extra.
>> - In vector, define (on top of _S_allocate_at_least) helper
>> functions _M_allocate_and_copy, _M_displace_storage,
>> and _M_allocate_and_migrate to abstract common ops, and
>> use them. This incidentally corrects an (apparent?) omission
>> of _GLIBCXX_ASAN_ANNOTATE_REINIT in operator=.
>> - Adjust tests to pass with or without allocate_at_least.
>> (__STDCPP_DEFAULT_NEW_ALIGNMENT__ is not defined everywhere.)
>>
>> Changes from RFC:
>> - Improve doxygen for new interfaces.
>> - Move code implementing alignment size-round-up logic from
>> allocator.h to alloc_traits.h for other allocators' reuse.
>> - Restore allocator.h definition of allocate_at_least, that
>> then explicitly delegates to its base class implementation.
>> - Retain existing string::_S_allocate, vector<>::_M_allocate
>> interfaces for ABI stability, using new names
>> _S_allocate_at_least, _M_allocate_at_least for new behavior.
>> - Define string _M_create_plus using S_allocate_at_least, and
>> use that throughout in place of _M_create, retaining _M_create
>> for ABI stability.
>> - Make memory_resource::allocate_at_least use argument
>> allocator's allocate_at_least if present.
>> - Fix std::allocator<>::allocate_at_least so it takes its
>> allocator object by reference, not by value.
>> - Per review, in constexpr context begin lifetime of all
>> potential string characters upon allocation.
>> - Revert "< 2011"-only vector<>::_M_initialize_dispatch changes.
>> - Fix string new-capacity off-by-one errors.
>> - Export new symbols _S_allocate_at_least and _M_create_plus from
>> bits/basic_string.h.
>> - Work around PR108377 spurious "exceeds maximum object size".
>> - Test.
>> - Patch too-strict capacity tests.
>>
>> Implement as much of allocator<>::allocate_at_least as possible
>> relying solely on known alignment behavior of standard operator
>> new. Provide apparatus for users' allocators to do the same.
>>
>> Use allocator_at_least in string and vector to maximize usage of
>> actually allocated storage, as revealed by the allocator in use.
>> For user-supplied allocators this may make a big difference.
>>
>> Fixes omitted _GLIBCXX_ASAN_ANNOTATE_REINIT in vector<>::operator=.
>>
>> Nothing is changed in include/ext/malloc_allocator or others.
>> They can be updated at leisure, piecemeal.
>>
>> libstdc++-v3/ChangeLog:
>> PR libstdc++/118030
>> * include/bits/alloc_traits.h (__allocate_to_alignment): Define.
>> (allocate_at_least): In allocator_traits template member,
>> destructure and reconstruct result from allocator object's version,
>> to enable pointer type conversion.
>> (allocate_at_least): Delegate, in allocator_traits<allocator<_Tp>>
>> specialization, to member where defined.
>> (allocate_at_least): Declare "= delete;" in allocator<void>.
>> * include/bits/allocator.h (allocate_at_least): Delegate to base
>> allocate_at_least where defined, calling with explicit base-class
>> qualification, picking up __new_allocator member.
>> * include/bits/basic_string.h (_Alloc_result): Define new type.
>> (_S_allocate_at_least): Define.
>> (_S_allocate): Delegate to _S_allocate_at_least.
>> (_M_create_plus, _M_create_and_place): Declare.
>> (assign): Use _S_allocate_at_least.
>> * include/bits/basic_string.tcc (_M_create_plus): Define.
>> (_M_create_and_place): Define, abstracting common operations.
>> (_M_replace, reserve): Use _S_allocate_at_least.
>> (_M_construct, input iterators): Use _M_create_plus.
>> (_M_construct, others (3x)): Use _M_create_and_place.
>> (_M_create, _M_assign, reserve, _M_mutate): Use _M_create_plus.
>> * include/bits/memory_resource.h (allocate_at_least): Define,
>> document.
>> * include/bits/memoryfwd.h (__allocate_to_alignment): Declare.
>> * include/bits/new_allocator.h (allocate_at_least): Define, relying
>> on __allocate_to_alignment from alloc_traits.h.
>> (deallocate): Refine "if constexpr" logic.
>> * include/bits/stl_vector.h:
>> (_S_max_size): Move to _Vector_base.
>> (_Alloc_result): Define type.
>> (_M_allocate_at_least): Define, using allocate_at_least where
>> supported.
>> (_M_allocate): Delegate to _M_allocate_at_least.
>> (max_size, _S_check_init_len): Use _S_max_size as moved.
>> (_M_create_storage, append_range, _M_allocate_and_copy,
>> _M_displace_storage): Define, abstracting common operations.
>> (_M_allocate_and_migrate): Define, likewise.
>> (_M_range_initialize_n): Use _M_allocate_at_least.
>> (_M_check_len): Improve logic.
>> * include/bits/vector.tcc:
>> (reserve, _M_fill_append, _M_range_insert): Use _M_allocate_at_least
>> and _M_displace_storage.
>> (operator=, _M_assign_aux): Use _M_allocate_and_migrate.
>> (_M_realloc_insert, _M_realloc_append, _M_default_append,
>> insert_range):
>> Use _M_allocate_at_least.
>> (_M_fill_insert): Use _M_displace_storage, normalize whitespace.
>> * include/std/string: Define __glibcxx_want_allocate_at_least.
>> * include/std/vector: Same.
>> * testsuite/util/testsuite_allocator.h
>> (allocate_at_least (2x)): Define.
>> (allocate): Use allocate_at_least.
>> * testsuite/20_util/allocator/allocate_at_least.cc: Add tests.
>> * testsuite/21_strings/basic_string/capacity/char/18654.cc:
>> Loosen capacity check.
>> * testsuite/21_strings/basic_string/capacity/char/shrink_to_fit.cc:
>> Same.
>> * testsuite/21_strings/basic_string/capacity/wchar_t/18654.cc: Same.
>> * testsuite/21_strings/basic_string/capacity/wchar_t/2.cc: Same.
>> * testsuite/21_strings/basic_string/capacity/wchar_t/
>> shrink_to_fit.cc:
>> Same.
>> * testsuite/23_containers/vector/capacity/shrink_to_fit.cc: Same.
>> * testsuite/23_containers/vector/capacity/shrink_to_fit2.cc: Same
>> * testsuite/23_containers/vector/modifiers/emplace/self_emplace.cc:
>> Adapt to looser reserve behavior.
>> * config/abi/pre/gnu.ver: Expose string::_S_allocate_at_least,
>> _M_create_plus, and _M_create_and_place symbols.
>> ---
>> libstdc++-v3/config/abi/pre/gnu.ver | 5 +
>> libstdc++-v3/include/bits/alloc_traits.h | 62 ++++++-
>> libstdc++-v3/include/bits/allocator.h | 28 +++-
>> libstdc++-v3/include/bits/basic_string.h | 38 ++++-
>> libstdc++-v3/include/bits/basic_string.tcc | 108 +++++++-----
>> libstdc++-v3/include/bits/memory_resource.h | 25 +++
>> libstdc++-v3/include/bits/memoryfwd.h | 6 +-
>> libstdc++-v3/include/bits/new_allocator.h | 41 ++++-
>> libstdc++-v3/include/bits/stl_vector.h | 132 +++++++++++----
>> libstdc++-v3/include/bits/vector.tcc | 155 +++++++-----------
>> libstdc++-v3/include/std/string | 1 +
>> libstdc++-v3/include/std/vector | 1 +
>> .../20_util/allocator/allocate_at_least.cc | 108 +++++++++++-
>> .../basic_string/capacity/char/18654.cc | 7 +-
>> .../capacity/char/shrink_to_fit.cc | 7 +-
>> .../basic_string/capacity/wchar_t/18654.cc | 8 +-
>> .../basic_string/capacity/wchar_t/2.cc | 13 +-
>> .../capacity/wchar_t/shrink_to_fit.cc | 7 +-
>> .../vector/capacity/shrink_to_fit.cc | 7 +-
>> .../vector/capacity/shrink_to_fit2.cc | 14 +-
>> .../vector/modifiers/emplace/self_emplace.cc | 36 ++--
>> .../testsuite/util/testsuite_allocator.h | 35 ++++
>> 22 files changed, 614 insertions(+), 230 deletions(-)
>>
>> diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/
>> config/abi/pre/gnu.ver
>> index 624de951d41..bfb710cc9bc 100644
>> --- a/libstdc++-v3/config/abi/pre/gnu.ver
>> +++ b/libstdc++-v3/config/abi/pre/gnu.ver
>> @@ -2606,6 +2606,11 @@ GLIBCXX_3.4.35 {
>>
>> _ZNSbIwSt11char_traitsIwESaIwEEC[12]EvQ26is_default_constructible_vIT1_E;
>>
>> _ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EEC[1-2]EvQ26is_default_constructible_vIT1_E;
>>
>> + # std::basic_string:_S_allocate_at_least
>> +
>> _ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE*_S_allocate_*;
>> +
>> _ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE*_M_create_*;
>
> This patch should wait for GCC 17 now, so the new symbols will need
> to be in a new symbol version, GLIBCXX_3.4.36. We can deal with that
> after the 16.1.0 release.
ACK.
>> #if defined (_WIN32) && !defined (__CYGWIN__)
>> _ZSt19__get_once_callablev;
>> _ZSt15__get_once_callv;
>> diff --git a/libstdc++-v3/include/bits/alloc_traits.h b/libstdc++-v3/
>> include/bits/alloc_traits.h
>> index 2be8ed561d4..3ded07eba3c 100644
>> --- a/libstdc++-v3/include/bits/alloc_traits.h
>> +++ b/libstdc++-v3/include/bits/alloc_traits.h
>> @@ -42,6 +42,9 @@
>> # include <bits/stl_iterator.h> // __make_move_if_noexcept_iterator
>> # endif
>> #endif
>> +#ifdef __glibcxx_allocate_at_least // C++23
>> +# include <bit> // for rotl
>> +#endif
>>
>> namespace std _GLIBCXX_VISIBILITY(default)
>> {
>> @@ -419,10 +422,14 @@ _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);
>> + {
>> + // Destructure to enable implicit __p -> pointer conversion.
>
> Why is any conversion needed here?
>
> allocator_traits::pointer has to be the same type as the allocator
> returns.
This appeared to be necessary to get tests involving apparatus in
testsuite/util/testsuite_allocator.* to build.
>> + auto [__p, __c] = __a.allocate_at_least(__n);
>> + return { __p, __c };
>> + }
>> else
>> return { __a.allocate(__n), __n };
>> }
>> @@ -664,15 +671,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> * @brief Allocate memory, generously.
>> * @param __a An allocator.
>> * @param __n The minimum number of objects to allocate space
>> for.
>> - * @return Memory of suitable size and alignment for `n` or more
>> - * contiguous objects of type `value_type`.
>> + * @return Memory of suitable size and alignment for `m >= n`
>> + * contiguous objects of type `value_type`, and `m`.
>> *
>> - * Returns `a.allocate_at_least(n)`.
>> + * Returns `a.allocate_at_least(n)` if that is well-formed,
>> + * or `{ a.allocate(n), n } otherwise.
>> */
>> - [[nodiscard]] static constexpr auto
>> - allocate_at_least(allocator_type __a, size_type __n)
>> - -> allocation_result<pointer, size_type>
>> - { return __a.allocate_at_least(__n); }
>> + [[nodiscard,__gnu__::__always_inline__]] static constexpr auto
>
> A newline after the attributes please
ACK.
>> + allocate_at_least(allocator_type& __a, size_type __n)
>> + -> allocation_result<pointer, size_type>
>> + {
>> + if constexpr (requires
>> + { __a.allocate_at_least(__n); })
>
> Do we need this condition?
>
> std::allocator guarantees that the member is available. A
> program-defined specialization of std::allocator needs to provide it
> to be valid in C++26.
ACK.
>> + return __a.allocate_at_least(__n);
>> + else
>> + return { __a.allocate(__n), __n };
>> + }
>> #endif
>>
>> /**
>> @@ -822,6 +836,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> static void*
>> allocate(allocator_type&, size_type, const void* = nullptr) =
>> delete;
>>
>> +#ifdef __glibcxx_allocate_at_least
>> + static allocation_result<pointer, size_type>
>> + allocate_at_least(allocator_type&, size_type) = delete;
>> +#endif
>> +
>> /// deallocate is ill-formed for allocator<void>
>> static void
>> deallocate(allocator_type&, void*, size_type) = delete;
>> @@ -874,6 +893,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> };
>> #endif
>>
>> +#ifdef __glibcxx_allocate_at_least
>> + template <typename _Pointer, typename _Size, typename _Alloc_fn>
>> + [[nodiscard]] constexpr auto
>> + __allocate_to_alignment(_Size __n, _Alloc_fn&& __af)
>
> This function is used in exactly one place, and is specific to
> operator new because of the use of __STDCPP_DEFAULT_NEW_ALIGNMENT__ so
> I think it would be better to just inline this code into
> __new_allocator::allocate_at_least, or make this a private static
> member function of __new_allocator.
>
> That way we know that n * objsize won't overflow (because we know that
> it's already been checked by __new_allocator, whereas here it's
> callable by any arbitrary code which might not have checked that).
>
> I also don't like passing in the allocation function, that seems ugly
> and potentially an optimization barrier.
I made a separate function because it seemed like it would be useful
for users upgrading their own allocators.
But I will fold it all back into __new_allocator::allocate_at_least,
and let users copy and paste from that.
>> + -> allocation_result<_Pointer, _Size>
>> + {
>> + const _Size __align_mask = __STDCPP_DEFAULT_NEW_ALIGNMENT__ - 1;
>> + const _Size __objsize = sizeof(*declval<_Pointer>());
>> + if constexpr (__objsize > __align_mask)
>> + return { __af(__n * __objsize), __n };
>> + else
>> + {
>> + const _Size __need = __n * __objsize;
>> + _Size __ask = (__need + __align_mask) & ~__align_mask;
>> + // Avoid rounding up to and asking for 2^63 bytes (PR108377):
>> + __ask -= rotl(__ask, 1) & 1;
>
> Does this optimize as well as:
>
> __ask -= bool(__ask & (_Size(1) << (__SIZE_WIDTH__ - 1)));
>
> ?
On x86, the rotl resolves to the same code, but does not depend
on how big _Size is. Folded into __new_allocator, there is no
uncertainty about that, so I have written
__ask -= __ask >> (__SIZE_WIDTH__ - 1);
which is shorter and resolves to one instruction less. Compilers
do not like generating a bit-test instruction for this, even though
that helpfully sets the carry bit; probably something about leaving
the instruction scheduler more freedom.
>> + using _U8 = const unsigned char;
>> + static_assert(__objsize <= ~_U8());
>> + // Use 8-bit division:
>
> Why?
Div instruction cycle count strongly depends on operand size,
particularly on smaller targets.
>> + _U8 __spare = __ask - __need, __size = __objsize;
>> + return { __af(__ask), __n + __spare / __size };
>> + }
>> + }
>> +#endif
>> +
>> /// @cond undocumented
>> #pragma GCC diagnostic push
>> #pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
>> diff --git a/libstdc++-v3/include/bits/allocator.h b/libstdc++-v3/
>> include/bits/allocator.h
>> index 9c22c805ebe..790edfaba41 100644
>> --- a/libstdc++-v3/include/bits/allocator.h
>> +++ b/libstdc++-v3/include/bits/allocator.h
>> @@ -193,9 +193,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> constexpr _Tp*
>> allocate(size_t __n)
>> {
>> -#if __cpp_concepts
>> +# if __cpp_concepts
>> if constexpr (requires { sizeof(_Tp); })
>> -#endif
>> +# endif
>> if (std::__is_constant_evaluated())
>> {
>> if (__builtin_mul_overflow(__n, sizeof(_Tp), &__n))
>> @@ -205,7 +205,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>
>> return __allocator_base<_Tp>::allocate(__n, 0);
>> }
>> +#endif
>> +
>> +#ifdef __glibcxx_allocate_at_least // C++23
>> + [[nodiscard,__gnu__::__always_inline__]]
>> + constexpr auto
>> + allocate_at_least(size_t __n)
>> + -> allocation_result<_Tp*, size_t>
>> + {
>> + if (std::is_constant_evaluated())
>
> It's preferable to use if consteval in code that doesn't need to
> compile as <= C++20. It is less work for the compiler.
ACK.
It is unfortunate how its demand for a block after its `else`
affects formatting.
>> + return { allocate(__n), __n };
>> + else if constexpr (requires
>> + { __allocator_base<_Tp>::allocate_at_least(__n); })
>> + return __allocator_base<_Tp>::allocate_at_least(__n);
>> + else
>> + return { __allocator_base<_Tp>::allocate(__n, 0), __n };
>
> You don't need to pass a hint argument to allocate
> (we might remove the second parameter some day, since the two-argument
> form of std::allocator::allocate was deprecated and removed).
ACK.
>> + }
>> +#endif
>>
>> +#if __cpp_constexpr_dynamic_alloc // >= C++20
>> [[__gnu__::__always_inline__]]
>> constexpr void
>> deallocate(_Tp* __p, size_t __n)
>> @@ -219,12 +237,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 af4e5d9486f..98bffa50f32 100644
>> --- a/libstdc++-v3/include/bits/basic_string.h
>> +++ b/libstdc++-v3/include/bits/basic_string.h
>> @@ -135,20 +135,35 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
>> #endif
>>
>> private:
>> + // For ABI reasons this must remain, though unused in active code.
>> static _GLIBCXX20_CONSTEXPR pointer
>> _S_allocate(_Char_alloc_type& __a, size_type __n)
>> + { return _S_allocate_at_least(__a, __n).__ptr; }
>
> This means that all callers of this function in objects compiled with
> older versions of GCC will now execute the extra instructions in
> allocate_at_least to round up the size, and then ignore it. That goes
> against "you don't pay for what you don't use".
>
> It seems better to keep the old behaviour of this function.
If restored, it would anyway only be used in a consteval context
in _M_replace.
>> +
>> + 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.
>> if constexpr (!is_same_v<_Traits, char_traits<_CharT>>)
>> 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]));
>> + for (size_type __i = 0; __i < __r.__count; ++__i)
>> + std::construct_at(__builtin_addressof(__r.__ptr[__i]));
>> #endif
>> - return __p;
>> + return __r;
>> }
>>
>> #ifdef __glibcxx_string_view // >= C++17
>> @@ -287,6 +302,15 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
>>
>> // Create & Destroy
>> _GLIBCXX20_CONSTEXPR
>> + _Alloc_result
>> + _M_create_plus(size_type __new_capacity, size_type
>> __old_capacity);
>> +
>> + _GLIBCXX20_CONSTEXPR
>> + void
>> + _M_create_and_place(size_type __new_capacity, size_type
>> __old_capacity);
>> +
>> + // This must remain for ABI stability though unused.
>> + _GLIBCXX20_CONSTEXPR
>> pointer
>> _M_create(size_type&, size_type);
>>
>> @@ -1782,10 +1806,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 - 1);
>> _M_set_length(__len);
>> }
>> }
>> diff --git a/libstdc++-v3/include/bits/basic_string.tcc b/libstdc++-
>> v3/include/bits/basic_string.tcc
>> index b00dd550237..aaf8df26b1a 100644
>> --- a/libstdc++-v3/include/bits/basic_string.tcc
>> +++ b/libstdc++-v3/include/bits/basic_string.tcc
>> @@ -139,14 +139,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>
>> template<typename _CharT, typename _Traits, typename _Alloc>
>> _GLIBCXX20_CONSTEXPR
>> - typename basic_string<_CharT, _Traits, _Alloc>::pointer
>> + typename basic_string<_CharT, _Traits, _Alloc>::_Alloc_result
>> basic_string<_CharT, _Traits, _Alloc>::
>> - _M_create(size_type& __capacity, size_type __old_capacity)
>> + _M_create_plus(size_type __capacity, size_type __old_capacity)
>> {
>> // _GLIBCXX_RESOLVE_LIB_DEFECTS
>> // 83. String::npos vs. string::max_size()
>> if (__capacity > max_size())
>> - std::__throw_length_error(__N("basic_string::_M_create"));
>> + std::__throw_length_error(__N("basic_string::_M_create_plus"));
>>
>> // The below implements an exponential growth policy, necessary to
>> // meet amortized linear time requirements of the library: see
>> @@ -161,7 +161,31 @@ _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);
>> + return _S_allocate_at_least(_M_get_allocator(), __capacity + 1);
>> + }
>> +
>> +
>> + template<typename _CharT, typename _Traits, typename _Alloc>
>> + _GLIBCXX20_CONSTEXPR
>> + void
>> + basic_string<_CharT, _Traits, _Alloc>::
>> + _M_create_and_place(size_type __capacity, size_type __old_capacity)
>> + {
>> + _Alloc_result __r = _M_create_plus(__capacity, __old_capacity);
>> + _M_data(__r.__ptr);
>> + _M_capacity(__r.__count - 1); // Leave room for NUL.
>> + }
>> +
>> + // This must remain for ABI stability, though unused in current code.
>> + template<typename _CharT, typename _Traits, typename _Alloc>
>> + _GLIBCXX20_CONSTEXPR
>> + typename basic_string<_CharT, _Traits, _Alloc>::pointer
>> + basic_string<_CharT, _Traits, _Alloc>::
>> + _M_create(size_type& __capacity, size_type __old_capacity)
>> + {
>> + _Alloc_result __r = _M_create_plus(__capacity, __old_capacity);
>
> Again, this makes existing callers of _M_create perform more work,
> although in this case they will actually use the extra capacity, so
> it's not wasted work.
Yes, _M_create performs much the same work as _M_create_plus, just
with a really awful usage interface.
>> + __capacity = __r.__count - 1; // Leave room for NUL.
>> + return __r.__ptr;
>> }
>>
>> // NB: This is the special case for Input Iterators, used in
>> @@ -203,11 +227,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> if (__len == __capacity)
>> {
>> // Allocate more space.
>> - __capacity = __len + 1;
>> - pointer __another = _M_create(__capacity, __len);
>> - this->_S_copy(__another, _M_data(), __len);
>> + _Alloc_result __another = _M_create_plus(__len + 1, __len);
>> + __capacity = __another.__count - 1; // Leave room for NUL.
>> + this->_S_copy(__another.__ptr, _M_data(), __len);
>> _M_dispose();
>> - _M_data(__another);
>> + _M_data(__another.__ptr);
>> _M_capacity(__capacity);
>> }
>> traits_type::assign(_M_data()[__len++],
>> @@ -231,10 +255,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> size_type __dnew = static_cast<size_type>(std::distance(__beg,
>> __end));
>>
>> if (__dnew > size_type(_S_local_capacity))
>> - {
>> - _M_data(_M_create(__dnew, size_type(0)));
>> - _M_capacity(__dnew);
>> - }
>> + _M_create_and_place(__dnew, size_type(0));
>> else
>> _M_init_local_buf();
>>
>> @@ -264,10 +285,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> _M_construct(size_type __n, _CharT __c)
>> {
>> if (__n > size_type(_S_local_capacity))
>> - {
>> - _M_data(_M_create(__n, size_type(0)));
>> - _M_capacity(__n);
>> - }
>> + _M_create_and_place(__n, size_type(0));
>> else
>> _M_init_local_buf();
>>
>> @@ -281,16 +299,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> // than difference between iterators.
>> template<typename _CharT, typename _Traits, typename _Alloc>
>> template<bool _Terminated>
>> - _GLIBCXX20_CONSTEXPR
>> + _GLIBCXX20_CONSTEXPR
>> void
>> basic_string<_CharT, _Traits, _Alloc>::
>> _M_construct(const _CharT* __str, size_type __n)
>> {
>> if (__n > size_type(_S_local_capacity))
>> - {
>> - _M_data(_M_create(__n, size_type(0)));
>> - _M_capacity(__n);
>> - }
>> + _M_create_and_place(__n, size_type(0));
>> else
>> _M_init_local_buf();
>>
>> @@ -347,11 +362,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>
>> if (__rsize > __capacity)
>> {
>> - size_type __new_capacity = __rsize;
>> - pointer __tmp = _M_create(__new_capacity, __capacity);
>> + // if _M_create_plus throws, there is no effect.
>> + _Alloc_result __tmp = _M_create_plus(__rsize, __capacity);
>> _M_dispose();
>> - _M_data(__tmp);
>> - _M_capacity(__new_capacity);
>> + _M_data(__tmp.__ptr);
>> + _M_capacity(__tmp.__count - 1);
>> }
>>
>> if (__rsize)
>> @@ -375,11 +390,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> if (__res <= __capacity)
>> return;
>>
>> - pointer __tmp = _M_create(__res, __capacity);
>> - this->_S_copy(__tmp, _M_data(), length() + 1);
>> + _Alloc_result __r = _M_create_plus(__res, __capacity);
>> + this->_S_copy(__r.__ptr, _M_data(), length() + 1);
>> _M_dispose();
>> - _M_data(__tmp);
>> - _M_capacity(__res);
>> + _M_data(__r.__ptr);
>> + _M_capacity(__r.__count - 1); // Leave room for NUL.
>> }
>>
>> template<typename _CharT, typename _Traits, typename _Alloc>
>> @@ -392,19 +407,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> const size_type __how_much = length() - __pos - __len1;
>>
>> size_type __new_capacity = length() + __len2 - __len1;
>> - pointer __r = _M_create(__new_capacity, capacity());
>> + _Alloc_result __r = _M_create_plus(__new_capacity, capacity());
>>
>> if (__pos)
>> - this->_S_copy(__r, _M_data(), __pos);
>> + this->_S_copy(__r.__ptr, _M_data(), __pos);
>> if (__s && __len2)
>> - this->_S_copy(__r + __pos, __s, __len2);
>> + this->_S_copy(__r.__ptr + __pos, __s, __len2);
>> if (__how_much)
>> - this->_S_copy(__r + __pos + __len2,
>> + this->_S_copy(__r.__ptr + __pos + __len2,
>> _M_data() + __pos + __len1, __how_much);
>>
>> _M_dispose();
>> - _M_data(__r);
>> - _M_capacity(__new_capacity);
>> + _M_data(__r.__ptr);
>> + _M_capacity(__r.__count - 1); // Leave room for NUL.
>> }
>>
>> template<typename _CharT, typename _Traits, typename _Alloc>
>> @@ -430,6 +445,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> if (_M_is_local())
>> return;
>>
>> +# ifdef __glibcxx_allocate_at_least // C++23
>> + const size_type __limit = (__STDCPP_DEFAULT_NEW_ALIGNMENT__ -
>> 1) / sizeof(_CharT);
>> +# else
>> + const size_type __limit = 0;
>> +# endif
>> const size_type __length = length();
>> const size_type __capacity = _M_allocated_capacity;
>>
>> @@ -441,14 +461,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> _M_data(_M_local_data());
>> }
>> #if __cpp_exceptions
>> - else if (__length < __capacity)
>> + else if (__capacity - __length > __limit )
>> 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 +609,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;
>
> If this branch isn't going to use the excess capacity, why bother
> using _S_allocate_at_least? _S_allocate seems simpler here.
This is a consteval context, where _S_allocate_at_least just calls
allocate(n) anyway. This would be the only use of _S_allocate,
otherwise only an ABI-stability stub.
>> _S_copy(__newp, this->_M_data(), __pos);
>> _S_copy(__newp + __pos, __s, __len2);
>> _S_copy(__newp + __pos + __len2, __p + __len1, __how_much);
>> @@ -677,7 +699,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> #endif // C++11
>>
>> #endif // _GLIBCXX_USE_CXX11_ABI
>> -
>> +
>> #if __glibcxx_constexpr_string >= 201907L
>> # define _GLIBCXX_STRING_CONSTEXPR constexpr
>> #else
>> @@ -916,7 +938,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> // Avoid reallocation for common case.
>> __str.erase();
>> _CharT __buf[128];
>> - __size_type __len = 0;
>> + __size_type __len = 0;
>> const streamsize __w = __in.width();
>> const __size_type __n = __w > 0 ? static_cast<__size_type>(__w)
>> : __str.max_size();
>> diff --git a/libstdc++-v3/include/bits/memory_resource.h b/libstdc++-
>> v3/include/bits/memory_resource.h
>> index e5c6697b07e..0a0f799437b 100644
>> --- a/libstdc++-v3/include/bits/memory_resource.h
>> +++ b/libstdc++-v3/include/bits/memory_resource.h
>> @@ -48,6 +48,7 @@
>> # include <bits/utility.h> // index_sequence
>> # include <tuple> // tuple, forward_as_tuple
>> #endif
>> +#include <bits/memoryfwd.h>
>>
>> namespace std _GLIBCXX_VISIBILITY(default)
>> {
>> @@ -468,6 +469,30 @@ 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 `m >= n`
>> + * objects of type `value_type`, and `m`.
>> + *
>> + * Returns `a.allocate_at_least(n)` if it exists, otherwise
>> + * `a.allocate(n)`.
>> + */
>> + [[nodiscard]] static auto
>> + allocate_at_least(allocator_type& __a, size_type __n)
>> + -> std::allocation_result<pointer, size_type>
>> + {
>> + if constexpr (requires { __a.allocate_at_least(__n); })
>
> pmr::polymorphic_allocator doesn't have allocate_at_least so this
> condition is never true.
>
> We do want allocate_at_least in this allocator_traits partial
> specialization but it should just return { __a.allocate(__n), __n}
> unconditionally.
ACK.
I guess when pmr::polymorphic_allocator gets allocate_at_least,
we can revisit.
>> + {
>> + auto [__p, __c] = __a.allocate_at_least(__n);
>> + return { __p, __c }; // Enable conversion en route.
>> + }
>> + else return { __a.allocate(__n), __n };
>> + }
>> +#endif
>> +
>> /**
>> * @brief Deallocate memory.
>> * @param __a An allocator.
>> diff --git a/libstdc++-v3/include/bits/memoryfwd.h b/libstdc++-v3/
>> include/bits/memoryfwd.h
>> index 9efe72b38ba..6742f75499e 100644
>> --- a/libstdc++-v3/include/bits/memoryfwd.h
>> +++ b/libstdc++-v3/include/bits/memoryfwd.h
>> @@ -88,8 +88,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> _Pointer ptr;
>> _Size count;
>> };
>> -#endif
>>
>> + template <typename _Pointer, typename _Size, typename _Alloc_fn>
>> + [[nodiscard]] constexpr auto
>> + __allocate_to_alignment(_Size __n, _Alloc_fn&& __af)
>> + -> allocation_result<_Pointer, _Size>;
>> +#endif
>>
>> /// @} group memory
>>
>> diff --git a/libstdc++-v3/include/bits/new_allocator.h b/libstdc++-v3/
>> include/bits/new_allocator.h
>> index fbe03e392aa..9a3f245f2aa 100644
>> --- a/libstdc++-v3/include/bits/new_allocator.h
>> +++ b/libstdc++-v3/include/bits/new_allocator.h
>> @@ -37,6 +37,7 @@
>> #if __cplusplus >= 201103L
>> #include <type_traits>
>> #endif
>> +#include <bits/memoryfwd.h>
>>
>> namespace std _GLIBCXX_VISIBILITY(default)
>> {
>> @@ -150,6 +151,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> std::__throw_bad_array_new_length();
>> std::__throw_bad_alloc();
>> }
>> +
>> #if __cpp_aligned_new && __cplusplus >= 201103L
>> else if constexpr (alignof(_Tp) > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
>> {
>> @@ -162,6 +164,41 @@ _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");
>> +
>> + if constexpr (!requires { sizeof(_Tp); })
>> + return { nullptr, 0 }; // static_assert already failed
>> + else if (__builtin_expect(__n > this->_M_max_size(), false))
>> + {
>> + // _GLIBCXX_RESOLVE_LIB_DEFECTS
>> + // 3190. allocator::allocate sometimes returns too little
>> storage
>> + if (__n > (std::size_t(-1) / sizeof(_Tp)))
>> + std::__throw_bad_array_new_length();
>> + std::__throw_bad_alloc();
>> + }
>> + else if (std::__is_constant_evaluated())
>
> This should be 'if consteval'
ACK.
>> + {
>> + auto __p = _GLIBCXX_OPERATOR_NEW(__n * sizeof(_Tp));
>> + return { static_cast<_Tp*>(__p), __n };
>> + }
>> + 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
>> + return __allocate_to_alignment<_Tp*>(__n, [](size_t __bytes)
>> + { return static_cast<_Tp*>(_GLIBCXX_OPERATOR_NEW(__bytes)); });
>> + }
>> +#endif
>> +
>> // __p is not permitted to be a null pointer.
>> _GLIBCXX20_CONSTEXPR void
>> deallocate(_Tp* __p, size_type __n __attribute__ ((__unused__)))
>> @@ -177,10 +214,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> {
>> _GLIBCXX_OPERATOR_DELETE(_GLIBCXX_SIZED_DEALLOC(__p, __n),
>> std::align_val_t(alignof(_Tp)));
>> - return;
>> }
>> + else
>> #endif
>> - _GLIBCXX_OPERATOR_DELETE(_GLIBCXX_SIZED_DEALLOC(__p, __n));
>> + _GLIBCXX_OPERATOR_DELETE(_GLIBCXX_SIZED_DEALLOC(__p, __n));
>> }
>>
>> #pragma GCC diagnostic pop
>> diff --git a/libstdc++-v3/include/bits/stl_vector.h b/libstdc++-v3/
>> include/bits/stl_vector.h
>> index c4ca214752a..4efe3d0f9d2 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
>> @@ -384,9 +397,37 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>> _GLIBCXX20_CONSTEXPR
>> pointer
>> _M_allocate(size_t __n)
>> + { return _M_allocate_at_least(__n).__ptr; }
>
> This also does the rounding up work, then ignores it.
ACK.
>> +
>> + struct _Alloc_result { pointer __ptr; size_t __count; };
>> +
>> + _GLIBCXX20_CONSTEXPR
>> + _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
>> + if constexpr (
>> + requires(_Tp_alloc_type& __a, size_t __n)
>> { _Tr::allocate_at_least(__a, __n); })
>> + {
>> + 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;
>
> Separate statements for the assignments please.
ACK.
>> + }
>> + else
>> +#endif
>> + __r.__ptr = _Tr::allocate(_M_impl, __n), __r.__count = __n;
>> + }
>> + else __r.__ptr = pointer(), __r.__count = 0;
>
> And again, although this line could be avoided by initializing __r
> using {}.
>
>> + return __r;
>> }
>>
>> _GLIBCXX20_CONSTEXPR
>> @@ -404,9 +445,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 +521,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;
>> @@ -536,6 +578,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>
>> 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 +1159,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 +1725,17 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>> return;
>> }
>>
>> - const size_type __len = _M_check_len(__n,
>> "vector::append_range");
>> + const size_type __ask = _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, __got] = this->_M_allocate_at_least(__ask);
>> + const pointer __start = __ptr;
>> const pointer __mid = __start + __sz;
>> const pointer __back = __mid + __n;
>> - _Guard_alloc __guard(__start, __len, *this);
>> + _Guard_alloc __guard(__start, __got, *this);
>> + allocator_type& __a = _M_get_Tp_allocator();
>> std::__uninitialized_copy_a(ranges::begin(__rg),
>> ranges::end(__rg),
>> __mid, __a);
>> @@ -1733,7 +1777,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>> // Finally, take ownership of new storage:
>> this->_M_impl._M_start = __start;
>> this->_M_impl._M_finish = __back;
>> - this->_M_impl._M_end_of_storage = __start + __len;
>> + this->_M_impl._M_end_of_storage = __start + __got;
>> }
>> else
>> {
>> @@ -1889,20 +1933,45 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>> protected:
>> /**
>> * Memory expansion handler. Uses the member allocation
>> function to
>> - * obtain @a n bytes of memory, and then copies [first,last)
>> into it.
>> + * obtain at least `n` objects worth of memory, copies
>> `[first,last)`
>> + * into it, reports what was actually allocated.
>> */
>> template<typename _ForwardIterator>
>> _GLIBCXX20_CONSTEXPR
>> - pointer
>> - _M_allocate_and_copy(size_type __n,
>> - _ForwardIterator __first, _ForwardIterator __last)
>> + _Alloc_result
>> + _M_allocate_and_copy(
>> + size_type __n, _ForwardIterator __first, _ForwardIterator __last)
>
> The parameter formatting was correct before this change.
ACK.
>> {
>> - _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();
>> + (void) __guard._M_release();
>> + return __r;
>> }
>>
>> + _GLIBCXX20_CONSTEXPR void
>> + _M_displace_storage(pointer __start, pointer __end, size_type
>> __cap)
>
> Would _M_replace_storage be a better name?
>
> "displace" has three meanings, one of which is to take the place of
> something else (the intended meaning here, where the new storage
> replaces the old), but another meaning is to just push something out
> of its place. So at the call site, if I read "displace storage" I
> don't know what it's doing.
ACK.
>> + {
>> + _GLIBCXX_ASAN_ANNOTATE_REINIT;
>> + _M_deallocate(this->_M_impl._M_start,
>> + this->_M_impl._M_end_of_storage - this->_M_impl._M_start);
>> + this->_M_impl._M_start = __start;
>> + this->_M_impl._M_finish = __end;
>> + this->_M_impl._M_end_of_storage = __start + __cap;
>> + }
>> +
>> + template<typename _ForwardIterator>
>> + _GLIBCXX20_CONSTEXPR
>> + void
>> + _M_allocate_and_migrate(
>
> This name doesn't tell me what it does either, "migrate" doesn't tell
> me that it makes a copy of [first, last) rather than migrating
> something *to* that range.
>
> Would _M_replace_with be better? The fact it allocates isn't really
> the more pertinent thing about this function, what it does is replace
> the current storage with new storage containing a copy of the range
> [first, last).
ACK.
>> + size_type __n, _ForwardIterator __first, _ForwardIterator __last)
>
> Incorrect formatting again.
ACK.
>> + {
>> + _Alloc_result __r = _M_allocate_and_copy(__n, __first, __last);
>> + std::_Destroy(this->_M_impl._M_start,
>> + this->_M_impl._M_finish, _M_get_Tp_allocator());
>> + _M_displace_storage(__r.__ptr, __r.__ptr + __n, __r.__count);
>> + }
>>
>> // Internal constructor functions follow.
>>
>> @@ -1971,10 +2040,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>> _M_range_initialize_n(_Iterator __first, _Sentinel __last,
>> size_pointer type.
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 +2261,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..86da54bdff1 100644
>> --- a/libstdc++-v3/include/bits/vector.tcc
>> +++ b/libstdc++-v3/include/bits/vector.tcc
>> @@ -75,13 +75,13 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>> if (this->capacity() < __n)
>> {
>> const size_type __old_size = size();
>> - pointer __tmp;
>> + _Alloc_result __tmp;
>> #if __cplusplus >= 201103L
>> if constexpr (_S_use_relocate())
>> {
>> - __tmp = this->_M_allocate(__n);
>> + __tmp = this->_M_allocate_at_least(__n);
>> std::__relocate_a(this->_M_impl._M_start, this-
>> >_M_impl._M_finish,
>> - __tmp, _M_get_Tp_allocator());
>> + __tmp.__ptr, _M_get_Tp_allocator());
>> }
>> else
>> #endif
>> @@ -92,13 +92,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>> std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish,
>> _M_get_Tp_allocator());
>> }
>> - _GLIBCXX_ASAN_ANNOTATE_REINIT;
>> - _M_deallocate(this->_M_impl._M_start,
>> - this->_M_impl._M_end_of_storage
>> - - this->_M_impl._M_start);
>> - this->_M_impl._M_start = __tmp;
>> - this->_M_impl._M_finish = __tmp + __old_size;
>> - this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n;
>> + _M_displace_storage(
>> + __tmp.__ptr, __tmp.__ptr + __old_size, __tmp.__count);
>> }
>> }
>> #pragma GCC diagnostic pop
>> @@ -106,12 +101,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 +120,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
>> @@ -238,22 +233,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>> #endif
>> const size_type __xlen = __x.size();
>> if (__xlen > capacity())
>> - {
>> - pointer __tmp = _M_allocate_and_copy(__xlen, __x.begin(),
>> - __x.end());
>> - std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish,
>> - _M_get_Tp_allocator());
>> - _M_deallocate(this->_M_impl._M_start,
>> - this->_M_impl._M_end_of_storage
>> - - this->_M_impl._M_start);
>> - this->_M_impl._M_start = __tmp;
>> - this->_M_impl._M_end_of_storage = this->_M_impl._M_start +
>> __xlen;
>> - }
>> + _M_allocate_and_migrate(__xlen, __x.begin(), __x.end());
>> else if (size() >= __xlen)
>> - {
>> - std::_Destroy(std::copy(__x.begin(), __x.end(), begin()),
>> - end(), _M_get_Tp_allocator());
>> - }
>> + std::_Destroy(std::copy(__x.begin(), __x.end(), begin()),
>> + end(), _M_get_Tp_allocator());
>> else
>> {
>> std::copy(__x._M_impl._M_start, __x._M_impl._M_start + size(),
>> @@ -332,16 +315,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>> __builtin_unreachable();
>>
>> _S_check_init_len(__len, _M_get_Tp_allocator());
>> - pointer __tmp(_M_allocate_and_copy(__len, __first, __last));
>> - std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish,
>> - _M_get_Tp_allocator());
>> - _GLIBCXX_ASAN_ANNOTATE_REINIT;
>> - _M_deallocate(this->_M_impl._M_start,
>> - this->_M_impl._M_end_of_storage
>> - - this->_M_impl._M_start);
>> - this->_M_impl._M_start = __tmp;
>> - this->_M_impl._M_finish = this->_M_impl._M_start + __len;
>> - this->_M_impl._M_end_of_storage = this->_M_impl._M_finish;
>> + _M_allocate_and_migrate(__len, __first, __last);
>> }
>> else if (__sz >= __len)
>> _M_erase_at_end(std::copy(__first, __last, this-
>> >_M_impl._M_start));
>> @@ -464,13 +438,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 +550,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 +630,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 +694,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
>> {
>> @@ -727,21 +707,16 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>> std::__uninitialized_fill_n_a(__new_start + __elems_before,
>> __n, __x,
>> _M_get_Tp_allocator());
>> - __new_finish = pointer();
>> -
>> - __new_finish
>> - = std::__uninitialized_move_if_noexcept_a
>> - (__old_start, __pos, __new_start, _M_get_Tp_allocator());
>> -
>> + __new_finish = pointer(); // ... in case of a throw.
>> + __new_finish = std::__uninitialized_move_if_noexcept_a(
>> + __old_start, __pos, __new_start, _M_get_Tp_allocator());
>> __new_finish += __n;
>> -
>> - __new_finish
>> - = std::__uninitialized_move_if_noexcept_a
>> - (__pos, __old_finish, __new_finish, _M_get_Tp_allocator());
>> + __new_finish = std::__uninitialized_move_if_noexcept_a(
>> + __pos, __old_finish, __new_finish, _M_get_Tp_allocator());
>> }
>> __catch(...)
>> {
>> - if (!__new_finish)
>> + if (__new_finish == pointer())
>> std::_Destroy(__new_start + __elems_before,
>> __new_start + __elems_before + __n,
>> _M_get_Tp_allocator());
>> @@ -752,12 +727,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>> __throw_exception_again;
>> }
>> std::_Destroy(__old_start, __old_finish,
>> _M_get_Tp_allocator());
>> - _GLIBCXX_ASAN_ANNOTATE_REINIT;
>> - _M_deallocate(__old_start,
>> - this->_M_impl._M_end_of_storage - __old_start);
>> - this->_M_impl._M_start = __new_start;
>> - this->_M_impl._M_finish = __new_finish;
>> - this->_M_impl._M_end_of_storage = __new_start + __len;
>> + _M_displace_storage(__new_start, __new_finish, __len);
>> }
>> }
>> }
>> @@ -785,9 +755,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>> pointer __old_finish = this->_M_impl._M_finish;
>> const size_type __old_size = __old_finish - __old_start;
>>
>> - const size_type __len =
>> - _M_check_len(__n, "vector::_M_fill_append");
>> - pointer __new_start(this->_M_allocate(__len));
>> + size_type __len = _M_check_len(__n, "vector::_M_fill_append");
>> + _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
>> {
>> @@ -807,12 +778,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>> __throw_exception_again;
>> }
>> std::_Destroy(__old_start, __old_finish, _M_get_Tp_allocator());
>> - _GLIBCXX_ASAN_ANNOTATE_REINIT;
>> - _M_deallocate(__old_start,
>> - this->_M_impl._M_end_of_storage - __old_start);
>> - this->_M_impl._M_start = __new_start;
>> - this->_M_impl._M_finish = __new_finish;
>> - this->_M_impl._M_end_of_storage = __new_start + __len;
>> + _M_displace_storage(__new_start, __new_finish, __len);
>> }
>> }
>>
>> @@ -852,9 +818,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 +971,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 __ask =
>> _M_check_len(__n, "vector::_M_range_insert");
>> #if __cplusplus < 201103L
>> - if (__len < (__n + (__old_finish - __old_start)))
>> + if (__ask < (__n + (__old_finish - __old_start)))
>> __builtin_unreachable();
>> #endif
>>
>> - pointer __new_start(this->_M_allocate(__len));
>> + _Alloc_result __r = this->_M_allocate_at_least(__ask);
>> + const size_type __got = __r.__count;
>> + pointer __new_start(__r.__ptr);
>> pointer __new_finish(__new_start);
>> __try
>> {
>> @@ -1031,17 +1001,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>> {
>> std::_Destroy(__new_start, __new_finish,
>> _M_get_Tp_allocator());
>> - _M_deallocate(__new_start, __len);
>> + _M_deallocate(__new_start, __got);
>> __throw_exception_again;
>> }
>> std::_Destroy(__old_start, __old_finish,
>> _M_get_Tp_allocator());
>> - _GLIBCXX_ASAN_ANNOTATE_REINIT;
>> - _M_deallocate(__old_start,
>> - this->_M_impl._M_end_of_storage - __old_start);
>> - this->_M_impl._M_start = __new_start;
>> - this->_M_impl._M_finish = __new_finish;
>> - this->_M_impl._M_end_of_storage = __new_start + __len;
>> + _M_displace_storage(__new_start, __new_finish, __got);
>> }
>> }
>> }
>> @@ -1111,7 +1076,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>> }
>> else // Reallocate
>> {
>> - const size_type __len
>> + const size_type __ask
>> = _M_check_len(__n, "vector::insert_range");
>>
>> struct _Guard : _Guard_alloc
>> @@ -1130,8 +1095,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>> };
>>
>> // Allocate new storage:
>> - pointer __new_start(this->_M_allocate(__len));
>> - _Guard __guard(__new_start, __len, *this);
>> + _Alloc_result __r = this->_M_allocate_at_least(__ask);
>> + const size_type __got = __r.__count;
>> + pointer __new_start(__r.__ptr);
>> + _Guard __guard(__new_start, __got, *this);
>>
>> auto& __alloc = _M_get_Tp_allocator();
>>
>> @@ -1158,13 +1125,13 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>> // with __guard so that it cleans up the old storage:
>> this->_M_impl._M_start = __guard._M_storage;
>> this->_M_impl._M_finish = __guard._M_finish;
>> - this->_M_impl._M_end_of_storage = __new_start + __len;
>> + this->_M_impl._M_end_of_storage = __new_start + __got;
>> __guard._M_storage = __old_start;
>> __guard._M_finish = __old_finish;
>> __guard._M_len = (__old_finish - __old_start) + __cap;
>> // _Asan::_Reinit destructor marks unused capacity.
>> // _Guard destructor destroys [old_start,old_finish).
>> - // _Guard_alloc destructor frees [old_start,old_start+len).
>> + // _Guard_alloc destructor frees [old_start,old_start+got).
>> }
>> return begin() + __ins_idx;
>> }
>> 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
>
> This macro should only defined in <memory> and <version>, not here.
ACK. I guess the specs for <string> and <vector> don't mention
allocate_at_least, making it an implemention detail?
>> #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
>
> And not here either.
ACK.
>> #define __glibcxx_want_allocator_traits_is_always_equal
>> #define __glibcxx_want_constexpr_vector
>> #define __glibcxx_want_containers_ranges
>> diff --git a/libstdc++-v3/testsuite/20_util/allocator/
>> allocate_at_least.cc b/libstdc++-v3/testsuite/20_util/allocator/
>> allocate_at_least.cc
>> index 5399096d294..987e2472490 100644
>> --- a/libstdc++-v3/testsuite/20_util/allocator/allocate_at_least.cc
>> +++ b/libstdc++-v3/testsuite/20_util/allocator/allocate_at_least.cc
>> @@ -29,7 +29,7 @@ template <typename T>
>> }
>> };
>>
>> -int main()
>> +void base()
>> {
>> std::allocator<X> native;
>> auto a1 = native.allocate_at_least(100);
>> @@ -63,3 +63,109 @@ int main()
>> VERIFY(a5.ptr == minimal.keep);
>> minimal_traits::deallocate(minimal, a5.ptr, a5.count);
>> }
>> +
>> +void extra()
>> +{
>> + using SatC = std::allocator_traits<std::allocator<char>>;
>> + std::allocator<char> satc;
>> + {
>> + auto [p, n] = SatC::allocate_at_least(satc, 1);
>> + VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__);
>> + SatC::deallocate(satc, p, n);
>> + }
>> + {
>> + auto [p, n] = SatC::allocate_at_least(satc, 2);
>> + VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__);
>> + SatC::deallocate(satc, p, n);
>> + }
>> + {
>> + auto [p, n] =
>> + SatC::allocate_at_least(satc, __STDCPP_DEFAULT_NEW_ALIGNMENT__
>> - 1);
>> + VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__);
>> + SatC::deallocate(satc, p, n);
>> + }
>> + {
>> + auto [p, n] = SatC::allocate_at_least(
>> + satc, __STDCPP_DEFAULT_NEW_ALIGNMENT__);
>> + VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__);
>> + SatC::deallocate(satc, p, n);
>> + }
>> +
>> + using SatS = std::allocator_traits<std::allocator<short>>;
>> + std::allocator<short> sats;
>> + {
>> + auto [p, n] = SatS::allocate_at_least(sats, 1);
>> + VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(short));
>> + SatS::deallocate(sats, p, n);
>> + }
>> + {
>> + auto [p, n] = SatS::allocate_at_least(sats, 2);
>> + VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(short));
>> + SatS::deallocate(sats, p, n);
>> + }
>> + {
>> + auto [p, n] = SatS::allocate_at_least(sats,
>> + (__STDCPP_DEFAULT_NEW_ALIGNMENT__ - 1) / sizeof(short));
>> + VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(short));
>> + SatS::deallocate(sats, p, n);
>> + }
>> + {
>> + auto [p, n] = SatS::allocate_at_least(sats,
>> + __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(short));
>> + VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(short));
>> + SatS::deallocate(sats, p, n);
>> + }
>> +
>> + struct A3 { char s[3]; };
>> + using SatA3 = std::allocator_traits<std::allocator<A3>>;
>> + std::allocator<A3> sata3;
>> + {
>> + auto [p, n] = SatA3::allocate_at_least(sata3, 1);
>> + VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(A3));
>> + SatA3::deallocate(sata3, p, n);
>> + }
>> + {
>> + auto [p, n] = SatA3::allocate_at_least(sata3, 2);
>> + VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(A3));
>> + SatA3::deallocate(sata3, p, n);
>> + }
>> + {
>> + auto [p, n] = SatA3::allocate_at_least(sata3,
>> + (__STDCPP_DEFAULT_NEW_ALIGNMENT__ - 1) / sizeof(A3));
>> + VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(A3));
>> + SatA3::deallocate(sata3, p, n);
>> + }
>> + {
>> + auto [p, n] = SatA3::allocate_at_least(sata3,
>> + __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(A3));
>> + VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(A3));
>> + SatA3::deallocate(sata3, p, n);
>> + }
>> +
>> + struct Anm1 { char s[__STDCPP_DEFAULT_NEW_ALIGNMENT__ - 1]; };
>> + using SatAnm1 = std::allocator_traits<std::allocator<Anm1>>;
>> + std::allocator<Anm1> satanm1;
>> + {
>> + auto [p, n] = SatAnm1::allocate_at_least(satanm1, 1);
>> + VERIFY(n == 1);
>> + SatAnm1::deallocate(satanm1, p, n);
>> + }
>> + {
>> + auto [p, n] = SatAnm1::allocate_at_least(satanm1,
>> + __STDCPP_DEFAULT_NEW_ALIGNMENT__);
>> + VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__);
>> + SatAnm1::deallocate(satanm1, p, n);
>> + }
>> + {
>> + auto [p, n] = SatAnm1::allocate_at_least(satanm1,
>> + __STDCPP_DEFAULT_NEW_ALIGNMENT__ - 1);
>> + VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__);
>> + SatAnm1::deallocate(satanm1, p, n);
>> + }
>> +}
>> +
>> +int main()
>> +{
>> + base();
>> + extra();
>> +}
>> diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/
>> char/18654.cc b/libstdc++-v3/testsuite/21_strings/basic_string/
>> capacity/char/18654.cc
>> index d542f34d08e..59012a1c170 100644
>> --- a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/
>> char/18654.cc
>> +++ b/libstdc++-v3/testsuite/21_strings/basic_string/capacity/
>> char/18654.cc
>> @@ -58,7 +58,12 @@ void test01()
>> #else
>> str.shrink_to_fit(); // reserve is deprecated in C++20
>> #endif
>> - VERIFY( str.capacity() == i );
>> +#if __glibcxx_allocate_at_least
>> + unsigned limit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ - 1;
>> +#else
>> + unsigned limit = 0;
>> +#endif
>> + VERIFY( str.capacity() - i <= limit);
>> }
>> }
>>
>> diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/
>> char/shrink_to_fit.cc b/libstdc++-v3/testsuite/21_strings/
>> basic_string/capacity/char/shrink_to_fit.cc
>> index a26d524dddf..ae5df746f35 100644
>> --- a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/char/
>> shrink_to_fit.cc
>> +++ b/libstdc++-v3/testsuite/21_strings/basic_string/capacity/char/
>> shrink_to_fit.cc
>> @@ -30,7 +30,12 @@ void test01()
>> s.push_back('b');
>> VERIFY( s.size() < s.capacity() );
>> s.shrink_to_fit();
>> - VERIFY( s.size() == s.capacity() );
>> +#ifdef __glibcxx_allocate_at_least
>> + unsigned limit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ - 1;
>> +#else
>> + unsigned limit = 0;
>> +#endif
>> + VERIFY( s.capacity() - s.size() <= limit );
>> }
>>
>> int main()
>> diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/
>> wchar_t/18654.cc b/libstdc++-v3/testsuite/21_strings/basic_string/
>> capacity/wchar_t/18654.cc
>> index 49e45c764c4..bf3b8dd5581 100644
>> --- a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/
>> wchar_t/18654.cc
>> +++ b/libstdc++-v3/testsuite/21_strings/basic_string/capacity/
>> wchar_t/18654.cc
>> @@ -50,6 +50,7 @@ void test01()
>> const size_type cap = str.capacity();
>> VERIFY( cap >= 3 * i );
>>
>> + // no shrink.
>> str.reserve(2 * i);
>> VERIFY( str.capacity() == cap );
>>
>> @@ -58,7 +59,12 @@ void test01()
>> #else
>> str.shrink_to_fit(); // reserve is deprecated in C++20
>> #endif
>> - VERIFY( str.capacity() == i );
>> +#if __glibcxx_allocate_at_least
>> + unsigned limit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ /
>> sizeof(wchar_t) - 1;
>> +#else
>> + unsigned limit = 0;
>> +#endif
>> + VERIFY( str.capacity() - i <= limit);
>> }
>> }
>>
>> diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/
>> wchar_t/2.cc b/libstdc++-v3/testsuite/21_strings/basic_string/
>> capacity/wchar_t/2.cc
>> index bff2bdd1862..04a3763fc77 100644
>> --- a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/
>> wchar_t/2.cc
>> +++ b/libstdc++-v3/testsuite/21_strings/basic_string/capacity/
>> wchar_t/2.cc
>> @@ -27,14 +27,21 @@
>> void test02()
>> {
>> std::wstring str01 = L"twelve chars";
>> - // str01 becomes shared
>> - std::wstring str02 = str01;
>> + str01.reserve(100);
>> #if __cplusplus <= 201703L
>> str01.reserve();
>> #else
>> str01.shrink_to_fit(); // reserve is deprecated in C++20
>> #endif
>> - VERIFY( str01.capacity() == 12 );
>> + // These are not guaranteed to absolutely minimize storage.
>> + // allocator<wchar_t>::allocate_at_least rounds up to what
>> + // it knows ::op new delivers.
>> +#ifdef __glibcxx_allocate_at_least
>> + unsigned limit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(wchar_t)
>> - 1;
>> +#else
>> + unsigned limit = 0;
>> +#endif
>> + VERIFY( str01.capacity() - str01.size() <= limit);
>> }
>>
>> int main()
>> diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/
>> wchar_t/shrink_to_fit.cc b/libstdc++-v3/testsuite/21_strings/
>> basic_string/capacity/wchar_t/shrink_to_fit.cc
>> index b4d0224a9d8..fc0f659539f 100644
>> --- a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/wchar_t/
>> shrink_to_fit.cc
>> +++ b/libstdc++-v3/testsuite/21_strings/basic_string/capacity/wchar_t/
>> shrink_to_fit.cc
>> @@ -30,7 +30,12 @@ void test01()
>> s.push_back(L'b');
>> VERIFY( s.size() < s.capacity() );
>> s.shrink_to_fit();
>> - VERIFY( s.size() == s.capacity() );
>> +#ifdef __glibcxx_allocate_at_least
>> + unsigned limit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(wchar_t)
>> - 1;
>> +#else
>> + unsigned limit = 0;
>> +#endif
>> + VERIFY( s.capacity() - s.size() <= limit );
>> }
>>
>> int main()
>> diff --git a/libstdc++-v3/testsuite/23_containers/vector/capacity/
>> shrink_to_fit.cc b/libstdc++-v3/testsuite/23_containers/vector/
>> capacity/shrink_to_fit.cc
>> index 74c68712b47..d4aa4d89b56 100644
>> --- a/libstdc++-v3/testsuite/23_containers/vector/capacity/
>> shrink_to_fit.cc
>> +++ b/libstdc++-v3/testsuite/23_containers/vector/capacity/
>> shrink_to_fit.cc
>> @@ -30,11 +30,12 @@ void test01()
>> v.push_back(1);
>> VERIFY( v.size() < v.capacity() );
>> v.shrink_to_fit();
>> -#if __cpp_exceptions
>> - VERIFY( v.size() == v.capacity() );
>> +#ifdef __glibcxx_allocate_at_least
>> + unsigned limit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(int);
>> #else
>> - VERIFY( v.size() < v.capacity() );
>> + unsigned limit = 0;
>> #endif
>> + VERIFY(v.capacity() - v.size() <= limit);
>> }
>>
>> int main()
>> diff --git a/libstdc++-v3/testsuite/23_containers/vector/capacity/
>> shrink_to_fit2.cc b/libstdc++-v3/testsuite/23_containers/vector/
>> capacity/shrink_to_fit2.cc
>> index c8faa9ded80..9489e043845 100644
>> --- a/libstdc++-v3/testsuite/23_containers/vector/capacity/
>> shrink_to_fit2.cc
>> +++ b/libstdc++-v3/testsuite/23_containers/vector/capacity/
>> shrink_to_fit2.cc
>> @@ -32,7 +32,12 @@ void test01()
>> v.reserve(100);
>> VERIFY( v.size() < v.capacity() );
>> v.shrink_to_fit();
>> - VERIFY( v.size() == v.capacity() );
>> +#ifdef __glibcxx_allocate_at_least
>> + unsigned limit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(int);
>> +#else
>> + unsigned limit = 0;
>> +#endif
>> + VERIFY( v.capacity() - v.size() <= limit);
>> VERIFY( v.get_allocator().get_personality() ==
>> alloc.get_personality() );
>> }
>>
>> @@ -45,7 +50,12 @@ void test02()
>> v.reserve(100);
>> VERIFY( v.size() < v.capacity() );
>> v.shrink_to_fit();
>> - VERIFY( v.size() == v.capacity() );
>> +#ifdef __glibcxx_allocate_at_least
>> + unsigned limit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(int);
>> +#else
>> + unsigned limit = 0;
>> +#endif
>> + VERIFY( v.capacity() - v.size() <= limit);
>> VERIFY( v.get_allocator().get_personality() ==
>> alloc.get_personality() );
>> }
>>
>> diff --git a/libstdc++-v3/testsuite/23_containers/vector/modifiers/
>> emplace/self_emplace.cc b/libstdc++-v3/testsuite/23_containers/vector/
>> modifiers/emplace/self_emplace.cc
>> index 00a0c7b06ea..629f35f05ba 100644
>> --- a/libstdc++-v3/testsuite/23_containers/vector/modifiers/emplace/
>> self_emplace.cc
>> +++ b/libstdc++-v3/testsuite/23_containers/vector/modifiers/emplace/
>> self_emplace.cc
>> @@ -99,37 +99,37 @@ struct A
>> void
>> test03()
>> {
>> - std::vector<A> va =
>> - {
>> - { A(1) },
>> - { A(2) },
>> - { A(3) }
>> - };
>> -
>> - // Make sure emplace will imply reallocation.
>> - VERIFY( va.capacity() == 3 );
>> +#ifdef __glibcxx_allocate_at_least
>> + unsigned fit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(A);
>> +#else
>> + unsigned fit = 4;
>> +#endif
>> + std::vector<A> va; va.reserve(fit);
>> + for (int i = 1; va.size() < va.capacity(); ++i)
>> + va.push_back(A(i));
>>
>> va.emplace(va.begin(), va.begin());
>>
>> - VERIFY( va.size() == 4 );
>> + VERIFY( va.size() == fit + 1 );
>> VERIFY( va[0]._i == 1 );
>> }
>>
>> void
>> test04()
>> {
>> - std::vector<A> va =
>> - {
>> - { A(1) },
>> - { A(2) },
>> - { A(3) }
>> - };
>> +#ifdef __glibcxx_allocate_at_least
>> + unsigned fit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(A);
>> +#else
>> + unsigned fit = 4;
>> +#endif
>> + std::vector<A> va; va.reserve(fit);
>> + for (int i = 1; va.size() < va.capacity() - 1; ++i)
>> + va.push_back(A(i));
>>
>> // Make sure emplace won't reallocate.
>> - va.reserve(4);
>> va.emplace(va.begin(), va.begin());
>>
>> - VERIFY( va.size() == 4 );
>> + VERIFY( va.size() == fit );
>> VERIFY( va[0]._i == 1 );
>> }
>>
>> diff --git a/libstdc++-v3/testsuite/util/testsuite_allocator.h b/
>> libstdc++-v3/testsuite/util/testsuite_allocator.h
>> index 892a385e307..a12099354ca 100644
>> --- a/libstdc++-v3/testsuite/util/testsuite_allocator.h
>> +++ b/libstdc++-v3/testsuite/util/testsuite_allocator.h
>> @@ -168,6 +168,16 @@ namespace __gnu_test
>> : Alloc(alloc)
>> { }
>>
>> +#ifdef __glibcxx_allocate_at_least // C++23
>> + std::allocation_result<pointer, size_type>
>> + allocate_at_least(size_type n)
>> + {
>> + auto [p, c] = AllocTraits::allocate_at_least(*this, n);
>> + counter_type::allocate(c * sizeof(T));
>> + return { p, c };
>> + }
>> +#endif
>> +
>> pointer
>> allocate(size_type n, const void* = 0)
>> {
>> @@ -373,6 +383,31 @@ namespace __gnu_test
>> return p;
>> }
>>
>> +#ifdef __glibcxx_allocate_at_least
>> + 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())
>
> if consteval
ACK.
>> + return r;
>> +
>> + try
>> + {
>> + get_map().insert(map_type::value_type(
>> + reinterpret_cast<void*>(r.ptr), personality));
>
> Huh, why do we use reinterpret_cast here when an implicit conversion
> or static_cast<void*> would work fine. But this is consistent with the
> existing code, so that's a question for another day.
ACK.
Probably p is some encapsulated thing.
>> + }
>> + catch(...)
>> + {
>> + AllocTraits::deallocate(*this, r.ptr, r.count);
>> + __throw_exception_again;
>> + }
>> +
>> + return r;
>> + }
>> +#endif
>> +
>> _GLIBCXX14_CONSTEXPR
>> void
>> deallocate(pointer p, size_type n)
>> --
>> 2.53.0
>>
>>
>
More information about the Libstdc++
mailing list