1// <memory_resource> -*- C++ -*-
2
3// Copyright (C) 2018-2024 Free Software Foundation, Inc.
4//
5// This file is part of the GNU ISO C++ Library. This library is free
6// software; you can redistribute it and/or modify it under the
7// terms of the GNU General Public License as published by the
8// Free Software Foundation; either version 3, or (at your option)
9// any later version.
10
11// This library is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// Under Section 7 of GPL version 3, you are granted additional
17// permissions described in the GCC Runtime Library Exception, version
18// 3.1, as published by the Free Software Foundation.
19
20// You should have received a copy of the GNU General Public License and
21// a copy of the GCC Runtime Library Exception along with this program;
22// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23// <http://www.gnu.org/licenses/>.
24
25/** @file include/bits/memory_resource.h
26 * This is an internal header file, included by other library headers.
27 * Do not attempt to use it directly. @headername{memory_resource}
28 */
29
30#ifndef _GLIBCXX_MEMORY_RESOURCE_H
31#define _GLIBCXX_MEMORY_RESOURCE_H 1
32
33#pragma GCC system_header
34
35#if __cplusplus >= 201703L
36
37#include <new> // operator new(size_t, void*)
38#include <cstddef> // size_t, max_align_t, byte
39#include <bits/functexcept.h> // __throw_bad_array_new_length
40#include <bits/uses_allocator.h> // allocator_arg_t, __use_alloc
41#include <bits/uses_allocator_args.h> // uninitialized_construct_using_alloc
42#include <ext/numeric_traits.h> // __int_traits
43#include <debug/assertions.h>
44
45#if ! __glibcxx_make_obj_using_allocator
46# include <bits/utility.h> // index_sequence
47# include <tuple> // tuple, forward_as_tuple
48#endif
49
50namespace std _GLIBCXX_VISIBILITY(default)
51{
52_GLIBCXX_BEGIN_NAMESPACE_VERSION
53namespace pmr
54{
55 /// Class `memory_resource`
56 /**
57 * @ingroup pmr
58 * @headerfile memory_resource
59 * @since C++17
60 */
61 class memory_resource
62 {
63 static constexpr size_t _S_max_align = alignof(max_align_t);
64
65 public:
66 memory_resource() = default;
67 memory_resource(const memory_resource&) = default;
68 virtual ~memory_resource(); // key function
69
70 memory_resource& operator=(const memory_resource&) = default;
71
72 [[nodiscard]]
73 void*
74 allocate(size_t __bytes, size_t __alignment = _S_max_align)
75 __attribute__((__returns_nonnull__,__alloc_size__(2),__alloc_align__(3)))
76 { return ::operator new(__bytes, p: do_allocate(__bytes, __alignment)); }
77
78 void
79 deallocate(void* __p, size_t __bytes, size_t __alignment = _S_max_align)
80 __attribute__((__nonnull__))
81 { return do_deallocate(__p, __bytes, __alignment); }
82
83 [[nodiscard]]
84 bool
85 is_equal(const memory_resource& __other) const noexcept
86 { return do_is_equal(__other); }
87
88 private:
89 virtual void*
90 do_allocate(size_t __bytes, size_t __alignment) = 0;
91
92 virtual void
93 do_deallocate(void* __p, size_t __bytes, size_t __alignment) = 0;
94
95 virtual bool
96 do_is_equal(const memory_resource& __other) const noexcept = 0;
97 };
98
99 [[nodiscard]]
100 inline bool
101 operator==(const memory_resource& __a, const memory_resource& __b) noexcept
102 { return &__a == &__b || __a.is_equal(other: __b); }
103
104#if __cpp_impl_three_way_comparison < 201907L
105 [[nodiscard]]
106 inline bool
107 operator!=(const memory_resource& __a, const memory_resource& __b) noexcept
108 { return !(__a == __b); }
109#endif
110
111 // C++17 23.12.3 Class template polymorphic_allocator
112
113 /// Class template polymorphic_allocator
114 /**
115 * @ingroup pmr
116 * @headerfile memory_resource
117 * @since C++17
118 */
119 template<typename _Tp>
120 class polymorphic_allocator
121 {
122 // _GLIBCXX_RESOLVE_LIB_DEFECTS
123 // 2975. Missing case for pair construction in polymorphic allocators
124 template<typename _Up>
125 struct __not_pair { using type = void; };
126
127 template<typename _Up1, typename _Up2>
128 struct __not_pair<pair<_Up1, _Up2>> { };
129
130 public:
131 using value_type = _Tp;
132
133 polymorphic_allocator() noexcept
134 {
135 extern memory_resource* get_default_resource() noexcept
136 __attribute__((__returns_nonnull__));
137 _M_resource = get_default_resource();
138 }
139
140 polymorphic_allocator(memory_resource* __r) noexcept
141 __attribute__((__nonnull__))
142 : _M_resource(__r)
143 { _GLIBCXX_DEBUG_ASSERT(__r); }
144
145 polymorphic_allocator(const polymorphic_allocator& __other) = default;
146
147 template<typename _Up>
148 polymorphic_allocator(const polymorphic_allocator<_Up>& __x) noexcept
149 : _M_resource(__x.resource())
150 { }
151
152 polymorphic_allocator&
153 operator=(const polymorphic_allocator&) = delete;
154
155 [[nodiscard]]
156 _Tp*
157 allocate(size_t __n)
158 __attribute__((__returns_nonnull__))
159 {
160 if ((__gnu_cxx::__int_traits<size_t>::__max / sizeof(_Tp)) < __n)
161 std::__throw_bad_array_new_length();
162 return static_cast<_Tp*>(_M_resource->allocate(bytes: __n * sizeof(_Tp),
163 alignment: alignof(_Tp)));
164 }
165
166 void
167 deallocate(_Tp* __p, size_t __n) noexcept
168 __attribute__((__nonnull__))
169 { _M_resource->deallocate(__p, bytes: __n * sizeof(_Tp), alignment: alignof(_Tp)); }
170
171#ifdef __glibcxx_polymorphic_allocator // >= C++20
172 [[nodiscard]] void*
173 allocate_bytes(size_t __nbytes,
174 size_t __alignment = alignof(max_align_t))
175 { return _M_resource->allocate(bytes: __nbytes, __alignment); }
176
177 void
178 deallocate_bytes(void* __p, size_t __nbytes,
179 size_t __alignment = alignof(max_align_t))
180 { _M_resource->deallocate(__p, bytes: __nbytes, __alignment); }
181
182 template<typename _Up>
183 [[nodiscard]] _Up*
184 allocate_object(size_t __n = 1)
185 {
186 if ((__gnu_cxx::__int_traits<size_t>::__max / sizeof(_Up)) < __n)
187 std::__throw_bad_array_new_length();
188 return static_cast<_Up*>(allocate_bytes(nbytes: __n * sizeof(_Up),
189 alignment: alignof(_Up)));
190 }
191
192 template<typename _Up>
193 void
194 deallocate_object(_Up* __p, size_t __n = 1)
195 { deallocate_bytes(__p, nbytes: __n * sizeof(_Up), alignment: alignof(_Up)); }
196
197 template<typename _Up, typename... _CtorArgs>
198 [[nodiscard]] _Up*
199 new_object(_CtorArgs&&... __ctor_args)
200 {
201 _Up* __p = allocate_object<_Up>();
202 __try
203 {
204 construct(__p, std::forward<_CtorArgs>(__ctor_args)...);
205 }
206 __catch (...)
207 {
208 deallocate_object(__p);
209 __throw_exception_again;
210 }
211 return __p;
212 }
213
214 template<typename _Up>
215 void
216 delete_object(_Up* __p)
217 {
218 __p->~_Up();
219 deallocate_object(__p);
220 }
221#endif // C++20
222
223#if ! __glibcxx_make_obj_using_allocator // >= C++20
224 template<typename _Tp1, typename... _Args>
225 __attribute__((__nonnull__))
226 typename __not_pair<_Tp1>::type
227 construct(_Tp1* __p, _Args&&... __args)
228 {
229 // _GLIBCXX_RESOLVE_LIB_DEFECTS
230 // 2969. polymorphic_allocator::construct() shouldn't pass resource()
231 using __use_tag
232 = std::__uses_alloc_t<_Tp1, polymorphic_allocator, _Args...>;
233 if constexpr (is_base_of_v<__uses_alloc0, __use_tag>)
234 ::new(__p) _Tp1(std::forward<_Args>(__args)...);
235 else if constexpr (is_base_of_v<__uses_alloc1_, __use_tag>)
236 ::new(__p) _Tp1(allocator_arg, *this,
237 std::forward<_Args>(__args)...);
238 else
239 ::new(__p) _Tp1(std::forward<_Args>(__args)..., *this);
240 }
241
242 template<typename _Tp1, typename _Tp2,
243 typename... _Args1, typename... _Args2>
244 __attribute__((__nonnull__))
245 void
246 construct(pair<_Tp1, _Tp2>* __p, piecewise_construct_t,
247 tuple<_Args1...> __x, tuple<_Args2...> __y)
248 {
249 auto __x_tag =
250 __use_alloc<_Tp1, polymorphic_allocator, _Args1...>(*this);
251 auto __y_tag =
252 __use_alloc<_Tp2, polymorphic_allocator, _Args2...>(*this);
253 index_sequence_for<_Args1...> __x_i;
254 index_sequence_for<_Args2...> __y_i;
255
256 ::new(__p) pair<_Tp1, _Tp2>(piecewise_construct,
257 _S_construct_p(__x_tag, __x_i, __x),
258 _S_construct_p(__y_tag, __y_i, __y));
259 }
260
261 template<typename _Tp1, typename _Tp2>
262 __attribute__((__nonnull__))
263 void
264 construct(pair<_Tp1, _Tp2>* __p)
265 { this->construct(__p, piecewise_construct, tuple<>(), tuple<>()); }
266
267 template<typename _Tp1, typename _Tp2, typename _Up, typename _Vp>
268 __attribute__((__nonnull__))
269 void
270 construct(pair<_Tp1, _Tp2>* __p, _Up&& __x, _Vp&& __y)
271 {
272 this->construct(__p, piecewise_construct,
273 std::forward_as_tuple(std::forward<_Up>(__x)),
274 std::forward_as_tuple(std::forward<_Vp>(__y)));
275 }
276
277 template <typename _Tp1, typename _Tp2, typename _Up, typename _Vp>
278 __attribute__((__nonnull__))
279 void
280 construct(pair<_Tp1, _Tp2>* __p, const std::pair<_Up, _Vp>& __pr)
281 {
282 this->construct(__p, piecewise_construct,
283 std::forward_as_tuple(__pr.first),
284 std::forward_as_tuple(__pr.second));
285 }
286
287 template<typename _Tp1, typename _Tp2, typename _Up, typename _Vp>
288 __attribute__((__nonnull__))
289 void
290 construct(pair<_Tp1, _Tp2>* __p, pair<_Up, _Vp>&& __pr)
291 {
292 this->construct(__p, piecewise_construct,
293 std::forward_as_tuple(std::forward<_Up>(__pr.first)),
294 std::forward_as_tuple(std::forward<_Vp>(__pr.second)));
295 }
296#else // make_obj_using_allocator
297 template<typename _Tp1, typename... _Args>
298 __attribute__((__nonnull__))
299 void
300 construct(_Tp1* __p, _Args&&... __args)
301 {
302 std::uninitialized_construct_using_allocator(__p, *this,
303 std::forward<_Args>(__args)...);
304 }
305#endif
306
307 template<typename _Up>
308 __attribute__((__nonnull__))
309 void
310 destroy(_Up* __p)
311 { __p->~_Up(); }
312
313 polymorphic_allocator
314 select_on_container_copy_construction() const noexcept
315 { return polymorphic_allocator(); }
316
317 memory_resource*
318 resource() const noexcept
319 __attribute__((__returns_nonnull__))
320 { return _M_resource; }
321
322 // _GLIBCXX_RESOLVE_LIB_DEFECTS
323 // 3683. operator== for polymorphic_allocator cannot deduce template arg
324 [[nodiscard]]
325 friend bool
326 operator==(const polymorphic_allocator& __a,
327 const polymorphic_allocator& __b) noexcept
328 { return *__a.resource() == *__b.resource(); }
329
330#if __cpp_impl_three_way_comparison < 201907L
331 [[nodiscard]]
332 friend bool
333 operator!=(const polymorphic_allocator& __a,
334 const polymorphic_allocator& __b) noexcept
335 { return !(__a == __b); }
336#endif
337
338 private:
339#if ! __glibcxx_make_obj_using_allocator // >= C++20
340 using __uses_alloc1_ = __uses_alloc1<polymorphic_allocator>;
341 using __uses_alloc2_ = __uses_alloc2<polymorphic_allocator>;
342
343 template<typename _Ind, typename... _Args>
344 static tuple<_Args&&...>
345 _S_construct_p(__uses_alloc0, _Ind, tuple<_Args...>& __t)
346 { return std::move(__t); }
347
348 template<size_t... _Ind, typename... _Args>
349 static tuple<allocator_arg_t, polymorphic_allocator, _Args&&...>
350 _S_construct_p(__uses_alloc1_ __ua, index_sequence<_Ind...>,
351 tuple<_Args...>& __t)
352 {
353 return {
354 allocator_arg, *__ua._M_a, std::get<_Ind>(std::move(__t))...
355 };
356 }
357
358 template<size_t... _Ind, typename... _Args>
359 static tuple<_Args&&..., polymorphic_allocator>
360 _S_construct_p(__uses_alloc2_ __ua, index_sequence<_Ind...>,
361 tuple<_Args...>& __t)
362 { return { std::get<_Ind>(std::move(__t))..., *__ua._M_a }; }
363#endif
364
365 memory_resource* _M_resource;
366 };
367
368 template<typename _Tp1, typename _Tp2>
369 [[nodiscard]]
370 inline bool
371 operator==(const polymorphic_allocator<_Tp1>& __a,
372 const polymorphic_allocator<_Tp2>& __b) noexcept
373 { return *__a.resource() == *__b.resource(); }
374
375#if __cpp_impl_three_way_comparison < 201907L
376 template<typename _Tp1, typename _Tp2>
377 [[nodiscard]]
378 inline bool
379 operator!=(const polymorphic_allocator<_Tp1>& __a,
380 const polymorphic_allocator<_Tp2>& __b) noexcept
381 { return !(__a == __b); }
382#endif
383
384} // namespace pmr
385
386 template<typename _Alloc> struct allocator_traits;
387
388 /// Partial specialization for `std::pmr::polymorphic_allocator`
389 /**
390 * @ingroup pmr
391 * @headerfile memory_resource
392 * @since C++17
393 */
394 template<typename _Tp>
395 struct allocator_traits<pmr::polymorphic_allocator<_Tp>>
396 {
397 /// The allocator type
398 using allocator_type = pmr::polymorphic_allocator<_Tp>;
399
400 /// The allocated type
401 using value_type = _Tp;
402
403 /// The allocator's pointer type.
404 using pointer = _Tp*;
405
406 /// The allocator's const pointer type.
407 using const_pointer = const _Tp*;
408
409 /// The allocator's void pointer type.
410 using void_pointer = void*;
411
412 /// The allocator's const void pointer type.
413 using const_void_pointer = const void*;
414
415 /// The allocator's difference type
416 using difference_type = std::ptrdiff_t;
417
418 /// The allocator's size type
419 using size_type = std::size_t;
420
421 /** @{
422 * A `polymorphic_allocator` does not propagate when a
423 * container is copied, moved, or swapped.
424 */
425 using propagate_on_container_copy_assignment = false_type;
426 using propagate_on_container_move_assignment = false_type;
427 using propagate_on_container_swap = false_type;
428
429 static allocator_type
430 select_on_container_copy_construction(const allocator_type&) noexcept
431 { return allocator_type(); }
432 /// @}
433
434 /// Whether all instances of the allocator type compare equal.
435 using is_always_equal = false_type;
436
437 template<typename _Up>
438 using rebind_alloc = pmr::polymorphic_allocator<_Up>;
439
440 template<typename _Up>
441 using rebind_traits = allocator_traits<pmr::polymorphic_allocator<_Up>>;
442
443 /**
444 * @brief Allocate memory.
445 * @param __a An allocator.
446 * @param __n The number of objects to allocate space for.
447 *
448 * Calls `a.allocate(n)`.
449 */
450 [[nodiscard]] static pointer
451 allocate(allocator_type& __a, size_type __n)
452 { return __a.allocate(__n); }
453
454 /**
455 * @brief Allocate memory.
456 * @param __a An allocator.
457 * @param __n The number of objects to allocate space for.
458 * @return Memory of suitable size and alignment for `n` objects
459 * of type `value_type`.
460 *
461 * The third parameter is ignored..
462 *
463 * Returns `a.allocate(n)`.
464 */
465 [[nodiscard]] static pointer
466 allocate(allocator_type& __a, size_type __n, const_void_pointer)
467 { return __a.allocate(__n); }
468
469 /**
470 * @brief Deallocate memory.
471 * @param __a An allocator.
472 * @param __p Pointer to the memory to deallocate.
473 * @param __n The number of objects space was allocated for.
474 *
475 * Calls `a.deallocate(p, n)`.
476 */
477 static void
478 deallocate(allocator_type& __a, pointer __p, size_type __n)
479 { __a.deallocate(__p, __n); }
480
481 /**
482 * @brief Construct an object of type `_Up`
483 * @param __a An allocator.
484 * @param __p Pointer to memory of suitable size and alignment for
485 * an object of type `_Up`.
486 * @param __args Constructor arguments.
487 *
488 * Calls `__a.construct(__p, std::forward<_Args>(__args)...)`
489 * in C++11, C++14 and C++17. Changed in C++20 to call
490 * `std::construct_at(__p, std::forward<_Args>(__args)...)` instead.
491 */
492 template<typename _Up, typename... _Args>
493 static void
494 construct(allocator_type& __a, _Up* __p, _Args&&... __args)
495 { __a.construct(__p, std::forward<_Args>(__args)...); }
496
497 /**
498 * @brief Destroy an object of type `_Up`
499 * @param __a An allocator.
500 * @param __p Pointer to the object to destroy
501 *
502 * Calls `p->_Up()`.
503 */
504 template<typename _Up>
505 static _GLIBCXX20_CONSTEXPR void
506 destroy(allocator_type&, _Up* __p)
507 noexcept(is_nothrow_destructible<_Up>::value)
508 { __p->~_Up(); }
509
510 /**
511 * @brief The maximum supported allocation size
512 * @return `numeric_limits<size_t>::max() / sizeof(value_type)`
513 */
514 static _GLIBCXX20_CONSTEXPR size_type
515 max_size(const allocator_type&) noexcept
516 { return size_t(-1) / sizeof(value_type); }
517 };
518
519_GLIBCXX_END_NAMESPACE_VERSION
520} // namespace std
521
522#endif // C++17
523#endif // _GLIBCXX_MEMORY_RESOURCE_H
524