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
sp_objects(std::size_t size)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>
sp_array_state(const U & _allocator,std::size_t _size)74 sp_array_state(const U& _allocator, std::size_t _size) BOOST_SP_NOEXCEPT
75 : allocator_(_allocator),
76 size_(_size) { }
77
allocator()78 A& allocator() BOOST_SP_NOEXCEPT {
79 return allocator_;
80 }
81
size() const82 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>
sp_size_array_state(const U & _allocator,std::size_t)97 sp_size_array_state(const U& _allocator, std::size_t) BOOST_SP_NOEXCEPT
98 : allocator_(_allocator) { }
99
allocator()100 A& allocator() BOOST_SP_NOEXCEPT {
101 return allocator_;
102 }
103
size() const104 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*
sp_array_start(T * base)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>
sp_array_creator(const U & other,std::size_t size)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
create()154 T* create() {
155 return reinterpret_cast<T*>(other_.allocate(size_));
156 }
157
destroy(T * base)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>
sp_array_base(const A & other,type * start,std::size_t size)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>
sp_array_base(const A & other,type * start,std::size_t size,const U & list)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
state()194 T& state() BOOST_SP_NOEXCEPT {
195 return state_;
196 }
197
dispose()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
destroy()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
get_deleter(const sp_typeinfo_ &)211 void* get_deleter(const sp_typeinfo_&) BOOST_SP_NOEXCEPT BOOST_OVERRIDE {
212 return 0;
213 }
214
get_local_deleter(const sp_typeinfo_ &)215 void* get_local_deleter(const sp_typeinfo_&)
216 BOOST_SP_NOEXCEPT BOOST_OVERRIDE {
217 return 0;
218 }
219
get_untyped_deleter()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>
sp_array_resultboost::detail::sp_array_result232 sp_array_result(const U& other, std::size_t size)
233 : creator_(other, size),
234 result_(creator_.create()) { }
235
~sp_array_resultboost::detail::sp_array_result236 ~sp_array_result() {
237 if (result_) {
238 creator_.destroy(result_);
239 }
240 }
241
getboost::detail::sp_array_result242 T* get() const BOOST_SP_NOEXCEPT {
243 return result_;
244 }
245
releaseboost::detail::sp_array_result246 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
allocate_shared(const A & allocator,std::size_t count)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
allocate_shared(const A & allocator)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
allocate_shared(const A & allocator,std::size_t count,const typename remove_extent<T>::type & value)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
allocate_shared(const A & allocator,const typename remove_extent<T>::type & value)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
allocate_shared_noinit(const A & allocator,std::size_t count)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
allocate_shared_noinit(const A & allocator)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