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#if __cplusplus > 201703L
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++2a
222
223#if ! __glibcxx_make_obj_using_allocator
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 _GLIBCXX20_DEPRECATED_SUGGEST("allocator_traits::destroy")
309 __attribute__((__nonnull__))
310 void
311 destroy(_Up* __p)
312 { __p->~_Up(); }
313
314 polymorphic_allocator
315 select_on_container_copy_construction() const noexcept
316 { return polymorphic_allocator(); }
317
318 memory_resource*
319 resource() const noexcept
320 __attribute__((__returns_nonnull__))
321 { return _M_resource; }
322
323 // _GLIBCXX_RESOLVE_LIB_DEFECTS
324 // 3683. operator== for polymorphic_allocator cannot deduce template arg
325 [[nodiscard]]
326 friend bool
327 operator==(const polymorphic_allocator& __a,
328 const polymorphic_allocator& __b) noexcept
329 { return *__a.resource() == *__b.resource(); }
330
331#if __cpp_impl_three_way_comparison < 201907L
332 [[nodiscard]]
333 friend bool
334 operator!=(const polymorphic_allocator& __a,
335 const polymorphic_allocator& __b) noexcept
336 { return !(__a == __b); }
337#endif
338
339 private:
340#if ! __glibcxx_make_obj_using_allocator
341 using __uses_alloc1_ = __uses_alloc1<polymorphic_allocator>;
342 using __uses_alloc2_ = __uses_alloc2<polymorphic_allocator>;
343
344 template<typename _Ind, typename... _Args>
345 static tuple<_Args&&...>
346 _S_construct_p(__uses_alloc0, _Ind, tuple<_Args...>& __t)
347 { return std::move(__t); }
348
349 template<size_t... _Ind, typename... _Args>
350 static tuple<allocator_arg_t, polymorphic_allocator, _Args&&...>
351 _S_construct_p(__uses_alloc1_ __ua, index_sequence<_Ind...>,
352 tuple<_Args...>& __t)
353 {
354 return {
355 allocator_arg, *__ua._M_a, std::get<_Ind>(std::move(__t))...
356 };
357 }
358
359 template<size_t... _Ind, typename... _Args>
360 static tuple<_Args&&..., polymorphic_allocator>
361 _S_construct_p(__uses_alloc2_ __ua, index_sequence<_Ind...>,
362 tuple<_Args...>& __t)
363 { return { std::get<_Ind>(std::move(__t))..., *__ua._M_a }; }
364#endif
365
366 memory_resource* _M_resource;
367 };
368
369 template<typename _Tp1, typename _Tp2>
370 [[nodiscard]]
371 inline bool
372 operator==(const polymorphic_allocator<_Tp1>& __a,
373 const polymorphic_allocator<_Tp2>& __b) noexcept
374 { return *__a.resource() == *__b.resource(); }
375
376#if __cpp_impl_three_way_comparison < 201907L
377 template<typename _Tp1, typename _Tp2>
378 [[nodiscard]]
379 inline bool
380 operator!=(const polymorphic_allocator<_Tp1>& __a,
381 const polymorphic_allocator<_Tp2>& __b) noexcept
382 { return !(__a == __b); }
383#endif
384
385} // namespace pmr
386
387 template<typename _Alloc> struct allocator_traits;
388
389 /// Partial specialization for std::pmr::polymorphic_allocator
390 template<typename _Tp>
391 struct allocator_traits<pmr::polymorphic_allocator<_Tp>>
392 {
393 /// The allocator type
394 using allocator_type = pmr::polymorphic_allocator<_Tp>;
395
396 /// The allocated type
397 using value_type = _Tp;
398
399 /// The allocator's pointer type.
400 using pointer = _Tp*;
401
402 /// The allocator's const pointer type.
403 using const_pointer = const _Tp*;
404
405 /// The allocator's void pointer type.
406 using void_pointer = void*;
407
408 /// The allocator's const void pointer type.
409 using const_void_pointer = const void*;
410
411 /// The allocator's difference type
412 using difference_type = std::ptrdiff_t;
413
414 /// The allocator's size type
415 using size_type = std::size_t;
416
417 /** @{
418 * A `polymorphic_allocator` does not propagate when a
419 * container is copied, moved, or swapped.
420 */
421 using propagate_on_container_copy_assignment = false_type;
422 using propagate_on_container_move_assignment = false_type;
423 using propagate_on_container_swap = false_type;
424
425 static allocator_type
426 select_on_container_copy_construction(const allocator_type&) noexcept
427 { return allocator_type(); }
428 /// @}
429
430 /// Whether all instances of the allocator type compare equal.
431 using is_always_equal = false_type;
432
433 template<typename _Up>
434 using rebind_alloc = pmr::polymorphic_allocator<_Up>;
435
436 template<typename _Up>
437 using rebind_traits = allocator_traits<pmr::polymorphic_allocator<_Up>>;
438
439 /**
440 * @brief Allocate memory.
441 * @param __a An allocator.
442 * @param __n The number of objects to allocate space for.
443 *
444 * Calls `a.allocate(n)`.
445 */
446 [[nodiscard]] static pointer
447 allocate(allocator_type& __a, size_type __n)
448 { return __a.allocate(__n); }
449
450 /**
451 * @brief Allocate memory.
452 * @param __a An allocator.
453 * @param __n The number of objects to allocate space for.
454 * @return Memory of suitable size and alignment for `n` objects
455 * of type `value_type`.
456 *
457 * The third parameter is ignored..
458 *
459 * Returns `a.allocate(n)`.
460 */
461 [[nodiscard]] static pointer
462 allocate(allocator_type& __a, size_type __n, const_void_pointer)
463 { return __a.allocate(__n); }
464
465 /**
466 * @brief Deallocate memory.
467 * @param __a An allocator.
468 * @param __p Pointer to the memory to deallocate.
469 * @param __n The number of objects space was allocated for.
470 *
471 * Calls `a.deallocate(p, n)`.
472 */
473 static void
474 deallocate(allocator_type& __a, pointer __p, size_type __n)
475 { __a.deallocate(__p, __n); }
476
477 /**
478 * @brief Construct an object of type `_Up`
479 * @param __a An allocator.
480 * @param __p Pointer to memory of suitable size and alignment for
481 * an object of type `_Up`.
482 * @param __args Constructor arguments.
483 *
484 * Calls `__a.construct(__p, std::forward<_Args>(__args)...)`
485 * in C++11, C++14 and C++17. Changed in C++20 to call
486 * `std::construct_at(__p, std::forward<_Args>(__args)...)` instead.
487 */
488 template<typename _Up, typename... _Args>
489 static void
490 construct(allocator_type& __a, _Up* __p, _Args&&... __args)
491 { __a.construct(__p, std::forward<_Args>(__args)...); }
492
493 /**
494 * @brief Destroy an object of type `_Up`
495 * @param __a An allocator.
496 * @param __p Pointer to the object to destroy
497 *
498 * Calls `p->_Up()`.
499 */
500 template<typename _Up>
501 static _GLIBCXX20_CONSTEXPR void
502 destroy(allocator_type&, _Up* __p)
503 noexcept(is_nothrow_destructible<_Up>::value)
504 { __p->~_Up(); }
505
506 /**
507 * @brief The maximum supported allocation size
508 * @return `numeric_limits<size_t>::max() / sizeof(value_type)`
509 */
510 static _GLIBCXX20_CONSTEXPR size_type
511 max_size(const allocator_type&) noexcept
512 { return size_t(-1) / sizeof(value_type); }
513 };
514
515_GLIBCXX_END_NAMESPACE_VERSION
516} // namespace std
517
518#endif // C++17
519#endif // _GLIBCXX_MEMORY_RESOURCE_H
520