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 | |
50 | namespace std _GLIBCXX_VISIBILITY(default) |
51 | { |
52 | _GLIBCXX_BEGIN_NAMESPACE_VERSION |
53 | namespace 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(__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, __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(__n * sizeof(_Up), |
189 | alignof(_Up))); |
190 | } |
191 | |
192 | template<typename _Up> |
193 | void |
194 | deallocate_object(_Up* __p, size_t __n = 1) |
195 | { deallocate_bytes(__p, __n * sizeof(_Up), 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 | |