1/*
2Copyright 2012-2017 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/shared_ptr.hpp>
12#include <boost/type_traits/alignment_of.hpp>
13#include <boost/type_traits/has_trivial_assign.hpp>
14#include <boost/type_traits/has_trivial_constructor.hpp>
15#include <boost/type_traits/has_trivial_destructor.hpp>
16#include <boost/type_traits/type_with_alignment.hpp>
17
18namespace boost {
19namespace detail {
20
21template<class>
22struct sp_if_array { };
23
24template<class T>
25struct sp_if_array<T[]> {
26 typedef boost::shared_ptr<T[]> type;
27};
28
29template<class>
30struct sp_if_size_array { };
31
32template<class T, std::size_t N>
33struct sp_if_size_array<T[N]> {
34 typedef boost::shared_ptr<T[N]> type;
35};
36
37template<class>
38struct sp_array_element { };
39
40template<class T>
41struct sp_array_element<T[]> {
42 typedef T type;
43};
44
45template<class T, std::size_t N>
46struct sp_array_element<T[N]> {
47 typedef T type;
48};
49
50template<class T>
51struct sp_array_scalar {
52 typedef T type;
53};
54
55template<class T, std::size_t N>
56struct sp_array_scalar<T[N]> {
57 typedef typename sp_array_scalar<T>::type type;
58};
59
60template<class T, std::size_t N>
61struct sp_array_scalar<const T[N]> {
62 typedef typename sp_array_scalar<T>::type type;
63};
64
65template<class T, std::size_t N>
66struct sp_array_scalar<volatile T[N]> {
67 typedef typename sp_array_scalar<T>::type type;
68};
69
70template<class T, std::size_t N>
71struct sp_array_scalar<const volatile T[N]> {
72 typedef typename sp_array_scalar<T>::type type;
73};
74
75template<class T>
76struct sp_array_scalar<T[]> {
77 typedef typename sp_array_scalar<T>::type type;
78};
79
80template<class T>
81struct sp_array_scalar<const T[]> {
82 typedef typename sp_array_scalar<T>::type type;
83};
84
85template<class T>
86struct sp_array_scalar<volatile T[]> {
87 typedef typename sp_array_scalar<T>::type type;
88};
89
90template<class T>
91struct sp_array_scalar<const volatile T[]> {
92 typedef typename sp_array_scalar<T>::type type;
93};
94
95template<class T>
96struct sp_array_count {
97 enum {
98 value = 1
99 };
100};
101
102template<class T, std::size_t N>
103struct sp_array_count<T[N]> {
104 enum {
105 value = N * sp_array_count<T>::value
106 };
107};
108
109template<std::size_t N, std::size_t M>
110struct sp_max_size {
111 enum {
112 value = N < M ? M : N
113 };
114};
115
116template<std::size_t N, std::size_t M>
117struct sp_align_up {
118 enum {
119 value = (N + M - 1) & ~(M - 1)
120 };
121};
122
123#if !defined(BOOST_NO_CXX11_ALLOCATOR)
124template<class A, class T>
125struct sp_bind_allocator {
126 typedef typename std::allocator_traits<A>::template rebind_alloc<T> type;
127};
128#else
129template<class A, class T>
130struct sp_bind_allocator {
131 typedef typename A::template rebind<T>::other type;
132};
133#endif
134
135template<class T>
136BOOST_CONSTEXPR inline std::size_t
137sp_objects(std::size_t size) BOOST_SP_NOEXCEPT
138{
139 return (size + sizeof(T) - 1) / sizeof(T);
140}
141
142template<bool, class = void>
143struct sp_enable { };
144
145template<class T>
146struct sp_enable<true, T> {
147 typedef T type;
148};
149
150template<bool E, class A, class T>
151inline typename sp_enable<!E && boost::has_trivial_destructor<T>::value>::type
152sp_array_destroy(A&, T*, std::size_t) BOOST_SP_NOEXCEPT { }
153
154template<bool E, class A, class T>
155inline typename sp_enable<!E &&
156 !boost::has_trivial_destructor<T>::value>::type
157sp_array_destroy(A&, T* start, std::size_t size)
158{
159 while (size > 0) {
160 start[--size].~T();
161 }
162}
163
164#if !defined(BOOST_NO_CXX11_ALLOCATOR)
165template<bool E, class A, class T>
166inline typename sp_enable<E>::type
167sp_array_destroy(A& allocator, T* start, std::size_t size)
168{
169 while (size > 0) {
170 std::allocator_traits<A>::destroy(allocator, start + --size);
171 }
172}
173#endif
174
175template<bool E, class A, class T>
176inline typename sp_enable<!E &&
177 boost::has_trivial_constructor<T>::value &&
178 boost::has_trivial_assign<T>::value>::type
179sp_array_construct(A&, T* start, std::size_t size)
180{
181 for (std::size_t i = 0; i < size; ++i) {
182 start[i] = T();
183 }
184}
185
186template<bool E, class A, class T>
187inline typename sp_enable<!E &&
188 boost::has_trivial_constructor<T>::value &&
189 boost::has_trivial_assign<T>::value>::type
190sp_array_construct(A&, T* start, std::size_t size, const T* list,
191 std::size_t count)
192{
193 for (std::size_t i = 0; i < size; ++i) {
194 start[i] = list[i % count];
195 }
196}
197
198#if !defined(BOOST_NO_EXCEPTIONS)
199template<bool E, class A, class T>
200inline typename sp_enable<!E &&
201 !(boost::has_trivial_constructor<T>::value &&
202 boost::has_trivial_assign<T>::value)>::type
203sp_array_construct(A& none, T* start, std::size_t size)
204{
205 std::size_t i = 0;
206 try {
207 for (; i < size; ++i) {
208 ::new(static_cast<void*>(start + i)) T();
209 }
210 } catch (...) {
211 sp_array_destroy<E>(none, start, i);
212 throw;
213 }
214}
215
216template<bool E, class A, class T>
217inline typename sp_enable<!E &&
218 !(boost::has_trivial_constructor<T>::value &&
219 boost::has_trivial_assign<T>::value)>::type
220sp_array_construct(A& none, T* start, std::size_t size, const T* list,
221 std::size_t count)
222{
223 std::size_t i = 0;
224 try {
225 for (; i < size; ++i) {
226 ::new(static_cast<void*>(start + i)) T(list[i % count]);
227 }
228 } catch (...) {
229 sp_array_destroy<E>(none, start, i);
230 throw;
231 }
232}
233#else
234template<bool E, class A, class T>
235inline typename sp_enable<!E &&
236 !(boost::has_trivial_constructor<T>::value &&
237 boost::has_trivial_assign<T>::value)>::type
238sp_array_construct(A&, T* start, std::size_t size)
239{
240 for (std::size_t i = 0; i < size; ++i) {
241 ::new(static_cast<void*>(start + i)) T();
242 }
243}
244
245template<bool E, class A, class T>
246inline typename sp_enable<!E &&
247 !(boost::has_trivial_constructor<T>::value &&
248 boost::has_trivial_assign<T>::value)>::type
249sp_array_construct(A&, T* start, std::size_t size, const T* list,
250 std::size_t count)
251{
252 for (std::size_t i = 0; i < size; ++i) {
253 ::new(static_cast<void*>(start + i)) T(list[i % count]);
254 }
255}
256#endif
257
258#if !defined(BOOST_NO_CXX11_ALLOCATOR)
259#if !defined(BOOST_NO_EXCEPTIONS)
260template<bool E, class A, class T>
261inline typename sp_enable<E>::type
262sp_array_construct(A& allocator, T* start, std::size_t size)
263{
264 std::size_t i = 0;
265 try {
266 for (i = 0; i < size; ++i) {
267 std::allocator_traits<A>::construct(allocator, start + i);
268 }
269 } catch (...) {
270 sp_array_destroy<E>(allocator, start, i);
271 throw;
272 }
273}
274
275template<bool E, class A, class T>
276inline typename sp_enable<E>::type
277sp_array_construct(A& allocator, T* start, std::size_t size, const T* list,
278 std::size_t count)
279{
280 std::size_t i = 0;
281 try {
282 for (i = 0; i < size; ++i) {
283 std::allocator_traits<A>::construct(allocator, start + i,
284 list[i % count]);
285 }
286 } catch (...) {
287 sp_array_destroy<E>(allocator, start, i);
288 throw;
289 }
290}
291#else
292template<bool E, class A, class T>
293inline typename sp_enable<E>::type
294sp_array_construct(A& allocator, T* start, std::size_t size)
295{
296 for (std::size_t i = 0; i < size; ++i) {
297 std::allocator_traits<A>::construct(allocator, start + i);
298 }
299}
300
301template<bool E, class A, class T>
302inline typename sp_enable<E>::type
303sp_array_construct(A& allocator, T* start, std::size_t size, const T* list,
304 std::size_t count)
305{
306 for (std::size_t i = 0; i < size; ++i) {
307 std::allocator_traits<A>::construct(allocator, start + i,
308 list[i % count]);
309 }
310}
311#endif
312#endif
313
314template<class A, class T>
315inline typename sp_enable<boost::has_trivial_constructor<T>::value>::type
316sp_array_default(A&, T*, std::size_t) BOOST_SP_NOEXCEPT { }
317
318#if !defined(BOOST_NO_EXCEPTIONS)
319template<class A, class T>
320inline typename sp_enable<!boost::has_trivial_constructor<T>::value>::type
321sp_array_default(A& none, T* start, std::size_t size)
322{
323 std::size_t i = 0;
324 try {
325 for (; i < size; ++i) {
326 ::new(static_cast<void*>(start + i)) T;
327 }
328 } catch (...) {
329 sp_array_destroy<false>(none, start, i);
330 throw;
331 }
332}
333#else
334template<bool E, class A, class T>
335inline typename sp_enable<!boost::has_trivial_constructor<T>::value>::type
336sp_array_default(A&, T* start, std::size_t size)
337{
338 for (std::size_t i = 0; i < size; ++i) {
339 ::new(static_cast<void*>(start + i)) T;
340 }
341}
342#endif
343
344template<class A>
345class sp_array_state {
346public:
347 typedef A type;
348
349 template<class U>
350 sp_array_state(const U& allocator, std::size_t size) BOOST_SP_NOEXCEPT
351 : allocator_(allocator),
352 size_(size) { }
353
354 A& allocator() BOOST_SP_NOEXCEPT {
355 return allocator_;
356 }
357
358 std::size_t size() const BOOST_SP_NOEXCEPT {
359 return size_;
360 }
361
362private:
363 A allocator_;
364 std::size_t size_;
365};
366
367template<class A, std::size_t N>
368class sp_size_array_state {
369public:
370 typedef A type;
371
372 template<class U>
373 sp_size_array_state(const U& allocator, std::size_t) BOOST_SP_NOEXCEPT
374 : allocator_(allocator) { }
375
376 A& allocator() BOOST_SP_NOEXCEPT {
377 return allocator_;
378 }
379
380 BOOST_CONSTEXPR std::size_t size() const BOOST_SP_NOEXCEPT {
381 return N;
382 }
383
384private:
385 A allocator_;
386};
387
388#if !defined(BOOST_NO_CXX11_ALLOCATOR)
389template<class A>
390struct sp_use_construct {
391 enum {
392 value = true
393 };
394};
395
396template<class T>
397struct sp_use_construct<std::allocator<T> > {
398 enum {
399 value = false
400 };
401};
402#else
403template<class>
404struct sp_use_construct {
405 enum {
406 value = false
407 };
408};
409#endif
410
411template<class T, class U>
412struct sp_array_alignment {
413 enum {
414 value = sp_max_size<boost::alignment_of<T>::value,
415 boost::alignment_of<U>::value>::value
416 };
417};
418
419template<class T, class U>
420struct sp_array_offset {
421 enum {
422 value = sp_align_up<sizeof(T), sp_array_alignment<T, U>::value>::value
423 };
424};
425
426template<class T, class U>
427struct sp_array_storage {
428 enum {
429 value = sp_array_alignment<T, U>::value
430 };
431 typedef typename boost::type_with_alignment<value>::type type;
432};
433
434template<class T, class U>
435inline U*
436sp_array_start(void* base) BOOST_SP_NOEXCEPT
437{
438 enum {
439 size = sp_array_offset<T, U>::value
440 };
441 return reinterpret_cast<U*>(static_cast<char*>(base) + size);
442}
443
444template<class A, class T>
445class sp_array_creator {
446 typedef typename A::value_type scalar;
447
448 enum {
449 offset = sp_array_offset<T, scalar>::value
450 };
451
452 typedef typename sp_array_storage<T, scalar>::type type;
453
454public:
455 template<class U>
456 sp_array_creator(const U& other, std::size_t size) BOOST_SP_NOEXCEPT
457 : other_(other),
458 size_(sp_objects<type>(offset + sizeof(scalar) * size)) { }
459
460 T* create() {
461 return reinterpret_cast<T*>(other_.allocate(size_));
462 }
463
464 void destroy(T* base) {
465 other_.deallocate(reinterpret_cast<type*>(base), size_);
466 }
467
468private:
469 typename sp_bind_allocator<A, type>::type other_;
470 std::size_t size_;
471};
472
473struct sp_default { };
474
475template<class T, bool E = sp_use_construct<T>::value>
476class sp_array_base
477 : public sp_counted_base {
478 typedef typename T::type allocator;
479
480public:
481 typedef typename allocator::value_type type;
482
483 template<class A>
484 sp_array_base(const A& other, std::size_t size, type* start)
485 : state_(other, size) {
486 sp_array_construct<E>(state_.allocator(), start, state_.size());
487 }
488
489 template<class A>
490 sp_array_base(const A& other, std::size_t size, const type* list,
491 std::size_t count, type* start)
492 : state_(other, size) {
493 sp_array_construct<E>(state_.allocator(), start, state_.size(), list,
494 count);
495 }
496
497 template<class A>
498 sp_array_base(sp_default, const A& other, std::size_t size, type* start)
499 : state_(other, size) {
500 sp_array_default(state_.allocator(), start, state_.size());
501 }
502
503 T& state() BOOST_SP_NOEXCEPT {
504 return state_;
505 }
506
507 virtual void dispose() {
508 sp_array_destroy<E>(state_.allocator(),
509 sp_array_start<sp_array_base, type>(this), state_.size());
510 }
511
512 virtual void destroy() {
513 sp_array_creator<allocator, sp_array_base> other(state_.allocator(),
514 state_.size());
515 this->~sp_array_base();
516 other.destroy(this);
517 }
518
519 virtual void* get_deleter(const sp_typeinfo&) {
520 return 0;
521 }
522
523 virtual void* get_local_deleter(const sp_typeinfo&) {
524 return 0;
525 }
526
527 virtual void* get_untyped_deleter() {
528 return 0;
529 }
530
531private:
532 T state_;
533};
534
535template<class A, class T>
536struct sp_array_result {
537public:
538 template<class U>
539 sp_array_result(const U& other, std::size_t size)
540 : creator_(other, size),
541 result_(creator_.create()) { }
542
543 ~sp_array_result() {
544 if (result_) {
545 creator_.destroy(result_);
546 }
547 }
548
549 T* get() const {
550 return result_;
551 }
552
553 void release() {
554 result_ = 0;
555 }
556
557private:
558 sp_array_result(const sp_array_result&);
559 sp_array_result& operator=(const sp_array_result&);
560
561 sp_array_creator<A, T> creator_;
562 T* result_;
563};
564
565} /* detail */
566
567template<class T, class A>
568inline typename detail::sp_if_array<T>::type
569allocate_shared(const A& allocator, std::size_t count)
570{
571 typedef typename detail::sp_array_element<T>::type type;
572 typedef typename detail::sp_array_scalar<T>::type scalar;
573 typedef typename detail::sp_bind_allocator<A, scalar>::type other;
574 typedef detail::sp_array_state<other> state;
575 typedef detail::sp_array_base<state> base;
576 std::size_t size = count * detail::sp_array_count<type>::value;
577 detail::sp_array_result<other, base> result(allocator, size);
578 detail::sp_counted_base* node = result.get();
579 scalar* start = detail::sp_array_start<base, scalar>(node);
580 ::new(static_cast<void*>(node)) base(allocator, size, start);
581 result.release();
582 return shared_ptr<T>(detail::sp_internal_constructor_tag(),
583 reinterpret_cast<type*>(start), detail::shared_count(node));
584}
585
586template<class T, class A>
587inline typename detail::sp_if_size_array<T>::type
588allocate_shared(const A& allocator)
589{
590 enum {
591 size = detail::sp_array_count<T>::value
592 };
593 typedef typename detail::sp_array_element<T>::type type;
594 typedef typename detail::sp_array_scalar<T>::type scalar;
595 typedef typename detail::sp_bind_allocator<A, scalar>::type other;
596 typedef detail::sp_size_array_state<other, size> state;
597 typedef detail::sp_array_base<state> base;
598 detail::sp_array_result<other, base> result(allocator, size);
599 detail::sp_counted_base* node = result.get();
600 scalar* start = detail::sp_array_start<base, scalar>(node);
601 ::new(static_cast<void*>(node)) base(allocator, size, start);
602 result.release();
603 return shared_ptr<T>(detail::sp_internal_constructor_tag(),
604 reinterpret_cast<type*>(start), detail::shared_count(node));
605}
606
607template<class T, class A>
608inline typename detail::sp_if_array<T>::type
609allocate_shared(const A& allocator, std::size_t count,
610 const typename detail::sp_array_element<T>::type& value)
611{
612 typedef typename detail::sp_array_element<T>::type type;
613 typedef typename detail::sp_array_scalar<T>::type scalar;
614 typedef typename detail::sp_bind_allocator<A, scalar>::type other;
615 typedef detail::sp_array_state<other> state;
616 typedef detail::sp_array_base<state> base;
617 std::size_t size = count * detail::sp_array_count<type>::value;
618 detail::sp_array_result<other, base> result(allocator, size);
619 detail::sp_counted_base* node = result.get();
620 scalar* start = detail::sp_array_start<base, scalar>(node);
621 ::new(static_cast<void*>(node)) base(allocator, size,
622 reinterpret_cast<const scalar*>(&value),
623 detail::sp_array_count<type>::value, start);
624 result.release();
625 return shared_ptr<T>(detail::sp_internal_constructor_tag(),
626 reinterpret_cast<type*>(start), detail::shared_count(node));
627}
628
629template<class T, class A>
630inline typename detail::sp_if_size_array<T>::type
631allocate_shared(const A& allocator,
632 const typename detail::sp_array_element<T>::type& value)
633{
634 enum {
635 size = detail::sp_array_count<T>::value
636 };
637 typedef typename detail::sp_array_element<T>::type type;
638 typedef typename detail::sp_array_scalar<T>::type scalar;
639 typedef typename detail::sp_bind_allocator<A, scalar>::type other;
640 typedef detail::sp_size_array_state<other, size> state;
641 typedef detail::sp_array_base<state> base;
642 detail::sp_array_result<other, base> result(allocator, size);
643 detail::sp_counted_base* node = result.get();
644 scalar* start = detail::sp_array_start<base, scalar>(node);
645 ::new(static_cast<void*>(node)) base(allocator, size,
646 reinterpret_cast<const scalar*>(&value),
647 detail::sp_array_count<type>::value, start);
648 result.release();
649 return shared_ptr<T>(detail::sp_internal_constructor_tag(),
650 reinterpret_cast<type*>(start), detail::shared_count(node));
651}
652
653template<class T, class A>
654inline typename detail::sp_if_array<T>::type
655allocate_shared_noinit(const A& allocator, std::size_t count)
656{
657 typedef typename detail::sp_array_element<T>::type type;
658 typedef typename detail::sp_array_scalar<T>::type scalar;
659 typedef typename detail::sp_bind_allocator<A, scalar>::type other;
660 typedef detail::sp_array_state<other> state;
661 typedef detail::sp_array_base<state, false> base;
662 std::size_t size = count * detail::sp_array_count<type>::value;
663 detail::sp_array_result<other, base> result(allocator, size);
664 detail::sp_counted_base* node = result.get();
665 scalar* start = detail::sp_array_start<base, scalar>(node);
666 ::new(static_cast<void*>(node)) base(detail::sp_default(), allocator,
667 size, start);
668 result.release();
669 return shared_ptr<T>(detail::sp_internal_constructor_tag(),
670 reinterpret_cast<type*>(start), detail::shared_count(node));
671}
672
673template<class T, class A>
674inline typename detail::sp_if_size_array<T>::type
675allocate_shared_noinit(const A& allocator)
676{
677 enum {
678 size = detail::sp_array_count<T>::value
679 };
680 typedef typename detail::sp_array_element<T>::type type;
681 typedef typename detail::sp_array_scalar<T>::type scalar;
682 typedef typename detail::sp_bind_allocator<A, scalar>::type other;
683 typedef detail::sp_size_array_state<other, size> state;
684 typedef detail::sp_array_base<state, false> base;
685 detail::sp_array_result<other, base> result(allocator, size);
686 detail::sp_counted_base* node = result.get();
687 scalar* start = detail::sp_array_start<base, scalar>(node);
688 ::new(static_cast<void*>(node)) base(detail::sp_default(), allocator,
689 size, start);
690 result.release();
691 return shared_ptr<T>(detail::sp_internal_constructor_tag(),
692 reinterpret_cast<type*>(start), detail::shared_count(node));
693}
694
695} /* boost */
696
697#endif
698