1 | /* |
2 | Copyright 2012-2019 Glen Joseph Fernandes |
3 | (glenjofe@gmail.com) |
4 | |
5 | Distributed under the Boost Software License, Version 1.0. |
6 | (http://www.boost.org/LICENSE_1_0.txt) |
7 | */ |
8 | #ifndef BOOST_SMART_PTR_ALLOCATE_SHARED_ARRAY_HPP |
9 | #define BOOST_SMART_PTR_ALLOCATE_SHARED_ARRAY_HPP |
10 | |
11 | #include <boost/core/allocator_access.hpp> |
12 | #include <boost/core/alloc_construct.hpp> |
13 | #include <boost/core/first_scalar.hpp> |
14 | #include <boost/smart_ptr/shared_ptr.hpp> |
15 | #include <boost/type_traits/alignment_of.hpp> |
16 | #include <boost/type_traits/enable_if.hpp> |
17 | #include <boost/type_traits/extent.hpp> |
18 | #include <boost/type_traits/is_bounded_array.hpp> |
19 | #include <boost/type_traits/is_unbounded_array.hpp> |
20 | #include <boost/type_traits/remove_cv.hpp> |
21 | #include <boost/type_traits/remove_extent.hpp> |
22 | #include <boost/type_traits/type_with_alignment.hpp> |
23 | |
24 | namespace boost { |
25 | namespace detail { |
26 | |
27 | template<class T> |
28 | struct sp_array_element { |
29 | typedef typename boost::remove_cv<typename |
30 | boost::remove_extent<T>::type>::type type; |
31 | }; |
32 | |
33 | template<class T> |
34 | struct sp_array_count { |
35 | enum { |
36 | value = 1 |
37 | }; |
38 | }; |
39 | |
40 | template<class T, std::size_t N> |
41 | struct sp_array_count<T[N]> { |
42 | enum { |
43 | value = N * sp_array_count<T>::value |
44 | }; |
45 | }; |
46 | |
47 | template<std::size_t N, std::size_t M> |
48 | struct sp_max_size { |
49 | enum { |
50 | value = N < M ? M : N |
51 | }; |
52 | }; |
53 | |
54 | template<std::size_t N, std::size_t M> |
55 | struct sp_align_up { |
56 | enum { |
57 | value = (N + M - 1) & ~(M - 1) |
58 | }; |
59 | }; |
60 | |
61 | template<class T> |
62 | BOOST_CONSTEXPR inline std::size_t |
63 | sp_objects(std::size_t size) BOOST_SP_NOEXCEPT |
64 | { |
65 | return (size + sizeof(T) - 1) / sizeof(T); |
66 | } |
67 | |
68 | template<class A> |
69 | class sp_array_state { |
70 | public: |
71 | typedef A type; |
72 | |
73 | template<class U> |
74 | sp_array_state(const U& _allocator, std::size_t _size) BOOST_SP_NOEXCEPT |
75 | : allocator_(_allocator), |
76 | size_(_size) { } |
77 | |
78 | A& allocator() BOOST_SP_NOEXCEPT { |
79 | return allocator_; |
80 | } |
81 | |
82 | std::size_t size() const BOOST_SP_NOEXCEPT { |
83 | return size_; |
84 | } |
85 | |
86 | private: |
87 | A allocator_; |
88 | std::size_t size_; |
89 | }; |
90 | |
91 | template<class A, std::size_t N> |
92 | class sp_size_array_state { |
93 | public: |
94 | typedef A type; |
95 | |
96 | template<class U> |
97 | sp_size_array_state(const U& _allocator, std::size_t) BOOST_SP_NOEXCEPT |
98 | : allocator_(_allocator) { } |
99 | |
100 | A& allocator() BOOST_SP_NOEXCEPT { |
101 | return allocator_; |
102 | } |
103 | |
104 | BOOST_CONSTEXPR std::size_t size() const BOOST_SP_NOEXCEPT { |
105 | return N; |
106 | } |
107 | |
108 | private: |
109 | A allocator_; |
110 | }; |
111 | |
112 | template<class T, class U> |
113 | struct sp_array_alignment { |
114 | enum { |
115 | value = sp_max_size<boost::alignment_of<T>::value, |
116 | boost::alignment_of<U>::value>::value |
117 | }; |
118 | }; |
119 | |
120 | template<class T, class U> |
121 | struct sp_array_offset { |
122 | enum { |
123 | value = sp_align_up<sizeof(T), sp_array_alignment<T, U>::value>::value |
124 | }; |
125 | }; |
126 | |
127 | template<class U, class T> |
128 | inline U* |
129 | sp_array_start(T* base) BOOST_SP_NOEXCEPT |
130 | { |
131 | enum { |
132 | size = sp_array_offset<T, U>::value |
133 | }; |
134 | return reinterpret_cast<U*>(reinterpret_cast<char*>(base) + size); |
135 | } |
136 | |
137 | template<class A, class T> |
138 | class sp_array_creator { |
139 | typedef typename A::value_type element; |
140 | |
141 | enum { |
142 | offset = sp_array_offset<T, element>::value |
143 | }; |
144 | |
145 | typedef typename boost::type_with_alignment<sp_array_alignment<T, |
146 | element>::value>::type type; |
147 | |
148 | public: |
149 | template<class U> |
150 | sp_array_creator(const U& other, std::size_t size) BOOST_SP_NOEXCEPT |
151 | : other_(other), |
152 | size_(sp_objects<type>(offset + sizeof(element) * size)) { } |
153 | |
154 | T* create() { |
155 | return reinterpret_cast<T*>(other_.allocate(size_)); |
156 | } |
157 | |
158 | void destroy(T* base) { |
159 | other_.deallocate(reinterpret_cast<type*>(base), size_); |
160 | } |
161 | |
162 | private: |
163 | typename boost::allocator_rebind<A, type>::type other_; |
164 | std::size_t size_; |
165 | }; |
166 | |
167 | template<class T> |
168 | class BOOST_SYMBOL_VISIBLE sp_array_base |
169 | : public sp_counted_base { |
170 | typedef typename T::type allocator; |
171 | |
172 | public: |
173 | typedef typename allocator::value_type type; |
174 | |
175 | template<class A> |
176 | sp_array_base(const A& other, type* start, std::size_t size) |
177 | : state_(other, size) { |
178 | boost::alloc_construct_n(state_.allocator(), |
179 | boost::first_scalar(start), |
180 | state_.size() * sp_array_count<type>::value); |
181 | } |
182 | |
183 | template<class A, class U> |
184 | sp_array_base(const A& other, type* start, std::size_t size, const U& list) |
185 | : state_(other, size) { |
186 | enum { |
187 | count = sp_array_count<type>::value |
188 | }; |
189 | boost::alloc_construct_n(state_.allocator(), |
190 | boost::first_scalar(start), state_.size() * count, |
191 | boost::first_scalar(&list), count); |
192 | } |
193 | |
194 | T& state() BOOST_SP_NOEXCEPT { |
195 | return state_; |
196 | } |
197 | |
198 | void dispose() BOOST_SP_NOEXCEPT BOOST_OVERRIDE { |
199 | boost::alloc_destroy_n(state_.allocator(), |
200 | boost::first_scalar(sp_array_start<type>(this)), |
201 | state_.size() * sp_array_count<type>::value); |
202 | } |
203 | |
204 | void destroy() BOOST_SP_NOEXCEPT BOOST_OVERRIDE { |
205 | sp_array_creator<allocator, sp_array_base> other(state_.allocator(), |
206 | state_.size()); |
207 | this->~sp_array_base(); |
208 | other.destroy(this); |
209 | } |
210 | |
211 | void* get_deleter(const sp_typeinfo_&) BOOST_SP_NOEXCEPT BOOST_OVERRIDE { |
212 | return 0; |
213 | } |
214 | |
215 | void* get_local_deleter(const sp_typeinfo_&) |
216 | BOOST_SP_NOEXCEPT BOOST_OVERRIDE { |
217 | return 0; |
218 | } |
219 | |
220 | void* get_untyped_deleter() BOOST_SP_NOEXCEPT BOOST_OVERRIDE { |
221 | return 0; |
222 | } |
223 | |
224 | private: |
225 | T state_; |
226 | }; |
227 | |
228 | template<class A, class T> |
229 | struct sp_array_result { |
230 | public: |
231 | template<class U> |
232 | sp_array_result(const U& other, std::size_t size) |
233 | : creator_(other, size), |
234 | result_(creator_.create()) { } |
235 | |
236 | ~sp_array_result() { |
237 | if (result_) { |
238 | creator_.destroy(result_); |
239 | } |
240 | } |
241 | |
242 | T* get() const BOOST_SP_NOEXCEPT { |
243 | return result_; |
244 | } |
245 | |
246 | void release() BOOST_SP_NOEXCEPT { |
247 | result_ = 0; |
248 | } |
249 | |
250 | private: |
251 | sp_array_result(const sp_array_result&); |
252 | sp_array_result& operator=(const sp_array_result&); |
253 | |
254 | sp_array_creator<A, T> creator_; |
255 | T* result_; |
256 | }; |
257 | |
258 | } /* detail */ |
259 | |
260 | template<class T, class A> |
261 | inline typename enable_if_<is_unbounded_array<T>::value, shared_ptr<T> >::type |
262 | allocate_shared(const A& allocator, std::size_t count) |
263 | { |
264 | typedef typename detail::sp_array_element<T>::type element; |
265 | typedef typename allocator_rebind<A, element>::type other; |
266 | typedef detail::sp_array_state<other> state; |
267 | typedef detail::sp_array_base<state> base; |
268 | detail::sp_array_result<other, base> result(allocator, count); |
269 | base* node = result.get(); |
270 | element* start = detail::sp_array_start<element>(node); |
271 | ::new(static_cast<void*>(node)) base(allocator, start, count); |
272 | result.release(); |
273 | return shared_ptr<T>(detail::sp_internal_constructor_tag(), start, |
274 | detail::shared_count(static_cast<detail::sp_counted_base*>(node))); |
275 | } |
276 | |
277 | template<class T, class A> |
278 | inline typename enable_if_<is_bounded_array<T>::value, shared_ptr<T> >::type |
279 | allocate_shared(const A& allocator) |
280 | { |
281 | enum { |
282 | count = extent<T>::value |
283 | }; |
284 | typedef typename detail::sp_array_element<T>::type element; |
285 | typedef typename allocator_rebind<A, element>::type other; |
286 | typedef detail::sp_size_array_state<other, extent<T>::value> state; |
287 | typedef detail::sp_array_base<state> base; |
288 | detail::sp_array_result<other, base> result(allocator, count); |
289 | base* node = result.get(); |
290 | element* start = detail::sp_array_start<element>(node); |
291 | ::new(static_cast<void*>(node)) base(allocator, start, count); |
292 | result.release(); |
293 | return shared_ptr<T>(detail::sp_internal_constructor_tag(), start, |
294 | detail::shared_count(static_cast<detail::sp_counted_base*>(node))); |
295 | } |
296 | |
297 | template<class T, class A> |
298 | inline typename enable_if_<is_unbounded_array<T>::value, shared_ptr<T> >::type |
299 | allocate_shared(const A& allocator, std::size_t count, |
300 | const typename remove_extent<T>::type& value) |
301 | { |
302 | typedef typename detail::sp_array_element<T>::type element; |
303 | typedef typename allocator_rebind<A, element>::type other; |
304 | typedef detail::sp_array_state<other> state; |
305 | typedef detail::sp_array_base<state> base; |
306 | detail::sp_array_result<other, base> result(allocator, count); |
307 | base* node = result.get(); |
308 | element* start = detail::sp_array_start<element>(node); |
309 | ::new(static_cast<void*>(node)) base(allocator, start, count, value); |
310 | result.release(); |
311 | return shared_ptr<T>(detail::sp_internal_constructor_tag(), start, |
312 | detail::shared_count(static_cast<detail::sp_counted_base*>(node))); |
313 | } |
314 | |
315 | template<class T, class A> |
316 | inline typename enable_if_<is_bounded_array<T>::value, shared_ptr<T> >::type |
317 | allocate_shared(const A& allocator, |
318 | const typename remove_extent<T>::type& value) |
319 | { |
320 | enum { |
321 | count = extent<T>::value |
322 | }; |
323 | typedef typename detail::sp_array_element<T>::type element; |
324 | typedef typename allocator_rebind<A, element>::type other; |
325 | typedef detail::sp_size_array_state<other, extent<T>::value> state; |
326 | typedef detail::sp_array_base<state> base; |
327 | detail::sp_array_result<other, base> result(allocator, count); |
328 | base* node = result.get(); |
329 | element* start = detail::sp_array_start<element>(node); |
330 | ::new(static_cast<void*>(node)) base(allocator, start, count, value); |
331 | result.release(); |
332 | return shared_ptr<T>(detail::sp_internal_constructor_tag(), start, |
333 | detail::shared_count(static_cast<detail::sp_counted_base*>(node))); |
334 | } |
335 | |
336 | template<class T, class A> |
337 | inline typename enable_if_<is_unbounded_array<T>::value, shared_ptr<T> >::type |
338 | allocate_shared_noinit(const A& allocator, std::size_t count) |
339 | { |
340 | return boost::allocate_shared<T>(boost::noinit_adapt(allocator), count); |
341 | } |
342 | |
343 | template<class T, class A> |
344 | inline typename enable_if_<is_bounded_array<T>::value, shared_ptr<T> >::type |
345 | allocate_shared_noinit(const A& allocator) |
346 | { |
347 | return boost::allocate_shared<T>(boost::noinit_adapt(allocator)); |
348 | } |
349 | |
350 | } /* boost */ |
351 | |
352 | #endif |
353 | |