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