1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef _LIBCPP___MEMORY_RESOURCE_POLYMORPHIC_ALLOCATOR_H
10 #define _LIBCPP___MEMORY_RESOURCE_POLYMORPHIC_ALLOCATOR_H
11 
12 #include <__assert>
13 #include <__availability>
14 #include <__config>
15 #include <__memory_resource/memory_resource.h>
16 #include <__utility/exception_guard.h>
17 #include <cstddef>
18 #include <limits>
19 #include <new>
20 #include <tuple>
21 
22 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
23 #  pragma GCC system_header
24 #endif
25 
26 _LIBCPP_PUSH_MACROS
27 #include <__undef_macros>
28 
29 #if _LIBCPP_STD_VER >= 17
30 
31 _LIBCPP_BEGIN_NAMESPACE_STD
32 
33 namespace pmr {
34 
35 // [mem.poly.allocator.class]
36 
37 template <class _ValueType
38 #  if _LIBCPP_STD_VER >= 20
39           = byte
40 #  endif
41           >
42 class _LIBCPP_AVAILABILITY_PMR _LIBCPP_TEMPLATE_VIS polymorphic_allocator {
43 
44 public:
45   using value_type = _ValueType;
46 
47   // [mem.poly.allocator.ctor]
48 
polymorphic_allocator()49   _LIBCPP_HIDE_FROM_ABI polymorphic_allocator() noexcept : __res_(std::pmr::get_default_resource()) {}
50 
polymorphic_allocator(memory_resource * __r)51   _LIBCPP_HIDE_FROM_ABI polymorphic_allocator(memory_resource* __r) noexcept : __res_(__r) {}
52 
53   _LIBCPP_HIDE_FROM_ABI polymorphic_allocator(const polymorphic_allocator&) = default;
54 
55   template <class _Tp>
polymorphic_allocator(const polymorphic_allocator<_Tp> & __other)56   _LIBCPP_HIDE_FROM_ABI polymorphic_allocator(const polymorphic_allocator<_Tp>& __other) noexcept
57       : __res_(__other.resource()) {}
58 
59   polymorphic_allocator& operator=(const polymorphic_allocator&) = delete;
60 
61   // [mem.poly.allocator.mem]
62 
allocate(size_t __n)63   _LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_HIDE_FROM_ABI _ValueType* allocate(size_t __n) {
64     if (__n > __max_size()) {
65       __throw_bad_array_new_length();
66     }
67     return static_cast<_ValueType*>(__res_->allocate(__n * sizeof(_ValueType), alignof(_ValueType)));
68   }
69 
deallocate(_ValueType * __p,size_t __n)70   _LIBCPP_HIDE_FROM_ABI void deallocate(_ValueType* __p, size_t __n) {
71     _LIBCPP_ASSERT_VALID_DEALLOCATION(
72         __n <= __max_size(),
73         "deallocate() called for a size which exceeds max_size(), leading to a memory leak "
74         "(the argument will overflow and result in too few objects being deleted)");
75     __res_->deallocate(__p, __n * sizeof(_ValueType), alignof(_ValueType));
76   }
77 
78 #  if _LIBCPP_STD_VER >= 20
79 
80   [[nodiscard]] [[using __gnu__: __alloc_size__(2), __alloc_align__(3)]] _LIBCPP_HIDE_FROM_ABI void*
81   allocate_bytes(size_t __nbytes, size_t __alignment = alignof(max_align_t)) {
82     return __res_->allocate(__nbytes, __alignment);
83   }
84 
85   _LIBCPP_HIDE_FROM_ABI void deallocate_bytes(void* __ptr, size_t __nbytes, size_t __alignment = alignof(max_align_t)) {
86     __res_->deallocate(__ptr, __nbytes, __alignment);
87   }
88 
89   template <class _Type>
90   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _Type* allocate_object(size_t __n = 1) {
91     if (numeric_limits<size_t>::max() / sizeof(_Type) < __n)
92       std::__throw_bad_array_new_length();
93     return static_cast<_Type*>(allocate_bytes(__n * sizeof(_Type), alignof(_Type)));
94   }
95 
96   template <class _Type>
97   _LIBCPP_HIDE_FROM_ABI void deallocate_object(_Type* __ptr, size_t __n = 1) {
98     deallocate_bytes(__ptr, __n * sizeof(_Type), alignof(_Type));
99   }
100 
101   template <class _Type, class... _CtorArgs>
new_object(_CtorArgs &&...__ctor_args)102   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _Type* new_object(_CtorArgs&&... __ctor_args) {
103     _Type* __ptr = allocate_object<_Type>();
104     auto __guard = std::__make_exception_guard([&] { deallocate_object(__ptr); });
105     construct(__ptr, std::forward<_CtorArgs>(__ctor_args)...);
106     __guard.__complete();
107     return __ptr;
108   }
109 
110   template <class _Type>
delete_object(_Type * __ptr)111   _LIBCPP_HIDE_FROM_ABI void delete_object(_Type* __ptr) {
112     destroy(__ptr);
113     deallocate_object(__ptr);
114   }
115 
116 #  endif // _LIBCPP_STD_VER >= 20
117 
118   template <class _Tp, class... _Ts>
construct(_Tp * __p,_Ts &&...__args)119   _LIBCPP_HIDE_FROM_ABI void construct(_Tp* __p, _Ts&&... __args) {
120     std::__user_alloc_construct_impl(
121         typename __uses_alloc_ctor<_Tp, polymorphic_allocator&, _Ts...>::type(),
122         __p,
123         *this,
124         std::forward<_Ts>(__args)...);
125   }
126 
127   template <class _T1, class _T2, class... _Args1, class... _Args2>
128   _LIBCPP_HIDE_FROM_ABI void
construct(pair<_T1,_T2> * __p,piecewise_construct_t,tuple<_Args1...> __x,tuple<_Args2...> __y)129   construct(pair<_T1, _T2>* __p, piecewise_construct_t, tuple<_Args1...> __x, tuple<_Args2...> __y) {
130     ::new ((void*)__p) pair<_T1, _T2>(
131         piecewise_construct,
132         __transform_tuple(typename __uses_alloc_ctor< _T1, polymorphic_allocator&, _Args1... >::type(),
133                           std::move(__x),
134                           typename __make_tuple_indices<sizeof...(_Args1)>::type{}),
135         __transform_tuple(typename __uses_alloc_ctor< _T2, polymorphic_allocator&, _Args2... >::type(),
136                           std::move(__y),
137                           typename __make_tuple_indices<sizeof...(_Args2)>::type{}));
138   }
139 
140   template <class _T1, class _T2>
construct(pair<_T1,_T2> * __p)141   _LIBCPP_HIDE_FROM_ABI void construct(pair<_T1, _T2>* __p) {
142     construct(__p, piecewise_construct, tuple<>(), tuple<>());
143   }
144 
145   template <class _T1, class _T2, class _Up, class _Vp>
construct(pair<_T1,_T2> * __p,_Up && __u,_Vp && __v)146   _LIBCPP_HIDE_FROM_ABI void construct(pair<_T1, _T2>* __p, _Up&& __u, _Vp&& __v) {
147     construct(__p,
148               piecewise_construct,
149               std::forward_as_tuple(std::forward<_Up>(__u)),
150               std::forward_as_tuple(std::forward<_Vp>(__v)));
151   }
152 
153   template <class _T1, class _T2, class _U1, class _U2>
construct(pair<_T1,_T2> * __p,const pair<_U1,_U2> & __pr)154   _LIBCPP_HIDE_FROM_ABI void construct(pair<_T1, _T2>* __p, const pair<_U1, _U2>& __pr) {
155     construct(__p, piecewise_construct, std::forward_as_tuple(__pr.first), std::forward_as_tuple(__pr.second));
156   }
157 
158   template <class _T1, class _T2, class _U1, class _U2>
construct(pair<_T1,_T2> * __p,pair<_U1,_U2> && __pr)159   _LIBCPP_HIDE_FROM_ABI void construct(pair<_T1, _T2>* __p, pair<_U1, _U2>&& __pr) {
160     construct(__p,
161               piecewise_construct,
162               std::forward_as_tuple(std::forward<_U1>(__pr.first)),
163               std::forward_as_tuple(std::forward<_U2>(__pr.second)));
164   }
165 
166   template <class _Tp>
destroy(_Tp * __p)167   _LIBCPP_HIDE_FROM_ABI void destroy(_Tp* __p) {
168     __p->~_Tp();
169   }
170 
select_on_container_copy_construction()171   _LIBCPP_HIDE_FROM_ABI polymorphic_allocator select_on_container_copy_construction() const noexcept {
172     return polymorphic_allocator();
173   }
174 
resource()175   _LIBCPP_HIDE_FROM_ABI memory_resource* resource() const noexcept { return __res_; }
176 
177 private:
178   template <class... _Args, size_t... _Is>
179   _LIBCPP_HIDE_FROM_ABI tuple<_Args&&...>
__transform_tuple(integral_constant<int,0>,tuple<_Args...> && __t,__tuple_indices<_Is...>)180   __transform_tuple(integral_constant<int, 0>, tuple<_Args...>&& __t, __tuple_indices<_Is...>) {
181     return std::forward_as_tuple(std::get<_Is>(std::move(__t))...);
182   }
183 
184   template <class... _Args, size_t... _Is>
185   _LIBCPP_HIDE_FROM_ABI tuple<allocator_arg_t const&, polymorphic_allocator&, _Args&&...>
__transform_tuple(integral_constant<int,1>,tuple<_Args...> && __t,__tuple_indices<_Is...>)186   __transform_tuple(integral_constant<int, 1>, tuple<_Args...>&& __t, __tuple_indices<_Is...>) {
187     using _Tup = tuple<allocator_arg_t const&, polymorphic_allocator&, _Args&&...>;
188     return _Tup(allocator_arg, *this, std::get<_Is>(std::move(__t))...);
189   }
190 
191   template <class... _Args, size_t... _Is>
192   _LIBCPP_HIDE_FROM_ABI tuple<_Args&&..., polymorphic_allocator&>
__transform_tuple(integral_constant<int,2>,tuple<_Args...> && __t,__tuple_indices<_Is...>)193   __transform_tuple(integral_constant<int, 2>, tuple<_Args...>&& __t, __tuple_indices<_Is...>) {
194     using _Tup = tuple<_Args&&..., polymorphic_allocator&>;
195     return _Tup(std::get<_Is>(std::move(__t))..., *this);
196   }
197 
__max_size()198   _LIBCPP_HIDE_FROM_ABI size_t __max_size() const noexcept {
199     return numeric_limits<size_t>::max() / sizeof(value_type);
200   }
201 
202   memory_resource* __res_;
203 };
204 
205 // [mem.poly.allocator.eq]
206 
207 template <class _Tp, class _Up>
208 inline _LIBCPP_HIDE_FROM_ABI bool
209 operator==(const polymorphic_allocator<_Tp>& __lhs, const polymorphic_allocator<_Up>& __rhs) noexcept {
210   return *__lhs.resource() == *__rhs.resource();
211 }
212 
213 #  if _LIBCPP_STD_VER <= 17
214 
215 template <class _Tp, class _Up>
216 inline _LIBCPP_HIDE_FROM_ABI bool
217 operator!=(const polymorphic_allocator<_Tp>& __lhs, const polymorphic_allocator<_Up>& __rhs) noexcept {
218   return !(__lhs == __rhs);
219 }
220 
221 #  endif
222 
223 } // namespace pmr
224 
225 _LIBCPP_END_NAMESPACE_STD
226 
227 #endif // _LIBCPP_STD_VER >= 17
228 
229 _LIBCPP_POP_MACROS
230 
231 #endif // _LIBCPP___MEMORY_RESOURCE_POLYMORPHIC_ALLOCATOR_H
232