boost/smart_ptr/allocate_shared_array.hpp
/* Copyright 2012-2019 Glen Joseph Fernandes (glenjofe@gmail.com) Distributed under the Boost Software License, Version 1.0. (http://www.boost.org/LICENSE_1_0.txt) */ #ifndef BOOST_SMART_PTR_ALLOCATE_SHARED_ARRAY_HPP #define BOOST_SMART_PTR_ALLOCATE_SHARED_ARRAY_HPP #include <boost/smart_ptr/shared_ptr.hpp> #include <boost/type_traits/alignment_of.hpp> #include <boost/type_traits/enable_if.hpp> #include <boost/type_traits/is_bounded_array.hpp> #include <boost/type_traits/is_unbounded_array.hpp> #include <boost/type_traits/has_trivial_assign.hpp> #include <boost/type_traits/has_trivial_constructor.hpp> #include <boost/type_traits/has_trivial_destructor.hpp> #include <boost/type_traits/remove_all_extents.hpp> #include <boost/type_traits/remove_cv.hpp> #include <boost/type_traits/remove_extent.hpp> #include <boost/type_traits/type_with_alignment.hpp> namespace boost { namespace detail { template<class T> struct sp_array_scalar { typedef typename boost::remove_cv<typename boost::remove_all_extents<T>::type>::type type; }; template<class T, class U> struct sp_array_count { enum { value = sizeof(T) / sizeof(U) }; }; template<std::size_t N, std::size_t M> struct sp_max_size { enum { value = N < M ? M : N }; }; template<std::size_t N, std::size_t M> struct sp_align_up { enum { value = (N + M - 1) & ~(M - 1) }; }; #if !defined(BOOST_NO_CXX11_ALLOCATOR) template<class A, class T> struct sp_bind_allocator { typedef typename std::allocator_traits<A>::template rebind_alloc<T> type; }; #else template<class A, class T> struct sp_bind_allocator { typedef typename A::template rebind<T>::other type; }; #endif template<class T> BOOST_CONSTEXPR inline std::size_t sp_objects(std::size_t size) BOOST_SP_NOEXCEPT { return (size + sizeof(T) - 1) / sizeof(T); } template<bool E, class A, class T> inline typename boost::enable_if_<!E && boost::has_trivial_destructor<T>::value>::type sp_array_destroy(A&, T*, std::size_t) BOOST_SP_NOEXCEPT { } template<bool E, class A, class T> inline typename boost::enable_if_<!E && !boost::has_trivial_destructor<T>::value>::type sp_array_destroy(A&, T* ptr, std::size_t size) { while (size > 0) { ptr[--size].~T(); } } #if !defined(BOOST_NO_CXX11_ALLOCATOR) template<bool E, class A, class T> inline typename boost::enable_if_<E>::type sp_array_destroy(A& allocator, T* ptr, std::size_t size) { while (size > 0) { std::allocator_traits<A>::destroy(allocator, ptr + --size); } } #endif template<bool E, class A, class T> class sp_destroyer { public: sp_destroyer(A& allocator, T* ptr) BOOST_SP_NOEXCEPT : allocator_(allocator), ptr_(ptr), size_(0) { } ~sp_destroyer() { sp_array_destroy<E>(allocator_, ptr_, size_); } std::size_t& size() BOOST_SP_NOEXCEPT { return size_; } private: sp_destroyer(const sp_destroyer&); sp_destroyer& operator=(const sp_destroyer&); A& allocator_; T* ptr_; std::size_t size_; }; template<bool E, class A, class T> inline typename boost::enable_if_<!E && boost::has_trivial_constructor<T>::value && boost::has_trivial_assign<T>::value && boost::has_trivial_destructor<T>::value>::type sp_array_construct(A&, T* ptr, std::size_t size) { for (std::size_t i = 0; i < size; ++i) { ptr[i] = T(); } } template<bool E, class A, class T> inline typename boost::enable_if_<!E && boost::has_trivial_constructor<T>::value && boost::has_trivial_assign<T>::value && boost::has_trivial_destructor<T>::value>::type sp_array_construct(A&, T* ptr, std::size_t size, const T* list, std::size_t count) { for (std::size_t i = 0; i < size; ++i) { ptr[i] = list[i % count]; } } template<bool E, class A, class T> inline typename boost::enable_if_<!E && !(boost::has_trivial_constructor<T>::value && boost::has_trivial_assign<T>::value && boost::has_trivial_destructor<T>::value)>::type sp_array_construct(A& none, T* ptr, std::size_t size) { sp_destroyer<E, A, T> hold(none, ptr); for (std::size_t& i = hold.size(); i < size; ++i) { ::new(static_cast<void*>(ptr + i)) T(); } hold.size() = 0; } template<bool E, class A, class T> inline typename boost::enable_if_<!E && !(boost::has_trivial_constructor<T>::value && boost::has_trivial_assign<T>::value && boost::has_trivial_destructor<T>::value)>::type sp_array_construct(A& none, T* ptr, std::size_t size, const T* list, std::size_t count) { sp_destroyer<E, A, T> hold(none, ptr); for (std::size_t& i = hold.size(); i < size; ++i) { ::new(static_cast<void*>(ptr + i)) T(list[i % count]); } hold.size() = 0; } #if !defined(BOOST_NO_CXX11_ALLOCATOR) template<bool E, class A, class T> inline typename boost::enable_if_<E>::type sp_array_construct(A& allocator, T* ptr, std::size_t size) { sp_destroyer<E, A, T> hold(allocator, ptr); for (std::size_t& i = hold.size(); i < size; ++i) { std::allocator_traits<A>::construct(allocator, ptr + i); } hold.size() = 0; } template<bool E, class A, class T> inline typename boost::enable_if_<E>::type sp_array_construct(A& allocator, T* ptr, std::size_t size, const T* list, std::size_t count) { sp_destroyer<E, A, T> hold(allocator, ptr); for (std::size_t& i = hold.size(); i < size; ++i) { std::allocator_traits<A>::construct(allocator, ptr + i, list[i % count]); } hold.size() = 0; } #endif template<class A, class T> inline typename boost::enable_if_<boost::has_trivial_constructor<T>::value>::type sp_array_default(A&, T*, std::size_t) BOOST_SP_NOEXCEPT { } template<class A, class T> inline typename boost::enable_if_<!boost::has_trivial_constructor<T>::value>::type sp_array_default(A& none, T* ptr, std::size_t size) { sp_destroyer<false, A, T> hold(none, ptr); for (std::size_t& i = hold.size(); i < size; ++i) { ::new(static_cast<void*>(ptr + i)) T; } hold.size() = 0; } template<class A> class sp_array_state { public: typedef A type; template<class U> sp_array_state(const U& _allocator, std::size_t _size) BOOST_SP_NOEXCEPT : allocator_(_allocator), size_(_size) { } A& allocator() BOOST_SP_NOEXCEPT { return allocator_; } std::size_t size() const BOOST_SP_NOEXCEPT { return size_; } private: A allocator_; std::size_t size_; }; template<class A, std::size_t N> class sp_size_array_state { public: typedef A type; template<class U> sp_size_array_state(const U& _allocator, std::size_t) BOOST_SP_NOEXCEPT : allocator_(_allocator) { } A& allocator() BOOST_SP_NOEXCEPT { return allocator_; } BOOST_CONSTEXPR std::size_t size() const BOOST_SP_NOEXCEPT { return N; } private: A allocator_; }; #if !defined(BOOST_NO_CXX11_ALLOCATOR) template<class A> struct sp_use_construct { enum { value = true }; }; template<class T> struct sp_use_construct<std::allocator<T> > { enum { value = false }; }; #else template<class> struct sp_use_construct { enum { value = false }; }; #endif template<class T, class U> struct sp_array_alignment { enum { value = sp_max_size<boost::alignment_of<T>::value, boost::alignment_of<U>::value>::value }; }; template<class T, class U> struct sp_array_offset { enum { value = sp_align_up<sizeof(T), sp_array_alignment<T, U>::value>::value }; }; template<class T, class U> struct sp_array_storage { enum { value = sp_array_alignment<T, U>::value }; typedef typename boost::type_with_alignment<value>::type type; }; template<class T, class U> inline U* sp_array_start(void* base) BOOST_SP_NOEXCEPT { enum { size = sp_array_offset<T, U>::value }; return reinterpret_cast<U*>(static_cast<char*>(base) + size); } template<class A, class T> class sp_array_creator { typedef typename A::value_type scalar; enum { offset = sp_array_offset<T, scalar>::value }; typedef typename sp_array_storage<T, scalar>::type type; public: template<class U> sp_array_creator(const U& other, std::size_t size) BOOST_SP_NOEXCEPT : other_(other), size_(sp_objects<type>(offset + sizeof(scalar) * size)) { } T* create() { return reinterpret_cast<T*>(other_.allocate(size_)); } void destroy(T* base) { other_.deallocate(reinterpret_cast<type*>(base), size_); } private: typename sp_bind_allocator<A, type>::type other_; std::size_t size_; }; struct sp_default { }; template<class T, bool E = sp_use_construct<T>::value> class BOOST_SYMBOL_VISIBLE sp_array_base : public sp_counted_base { typedef typename T::type allocator; public: typedef typename allocator::value_type type; template<class A> sp_array_base(const A& other, std::size_t size, type* start) : state_(other, size) { sp_array_construct<E>(state_.allocator(), start, state_.size()); } template<class A> sp_array_base(const A& other, std::size_t size, const type* list, std::size_t count, type* start) : state_(other, size) { sp_array_construct<E>(state_.allocator(), start, state_.size(), list, count); } template<class A> sp_array_base(sp_default, const A& other, std::size_t size, type* start) : state_(other, size) { sp_array_default(state_.allocator(), start, state_.size()); } T& state() BOOST_SP_NOEXCEPT { return state_; } virtual void dispose() BOOST_SP_NOEXCEPT { sp_array_destroy<E>(state_.allocator(), sp_array_start<sp_array_base, type>(this), state_.size()); } virtual void destroy() BOOST_SP_NOEXCEPT { sp_array_creator<allocator, sp_array_base> other(state_.allocator(), state_.size()); this->~sp_array_base(); other.destroy(this); } virtual void* get_deleter(const sp_typeinfo&) BOOST_SP_NOEXCEPT { return 0; } virtual void* get_local_deleter(const sp_typeinfo&) BOOST_SP_NOEXCEPT { return 0; } virtual void* get_untyped_deleter() BOOST_SP_NOEXCEPT { return 0; } private: T state_; }; template<class A, class T> struct sp_array_result { public: template<class U> sp_array_result(const U& other, std::size_t size) : creator_(other, size), result_(creator_.create()) { } ~sp_array_result() { if (result_) { creator_.destroy(result_); } } T* get() const { return result_; } void release() { result_ = 0; } private: sp_array_result(const sp_array_result&); sp_array_result& operator=(const sp_array_result&); sp_array_creator<A, T> creator_; T* result_; }; } /* detail */ template<class T, class A> inline typename enable_if_<is_unbounded_array<T>::value, shared_ptr<T> >::type allocate_shared(const A& allocator, std::size_t count) { typedef typename remove_extent<T>::type type; typedef typename detail::sp_array_scalar<T>::type scalar; typedef typename detail::sp_bind_allocator<A, scalar>::type other; typedef detail::sp_array_state<other> state; typedef detail::sp_array_base<state> base; std::size_t size = count * detail::sp_array_count<type, scalar>::value; detail::sp_array_result<other, base> result(allocator, size); detail::sp_counted_base* node = result.get(); scalar* start = detail::sp_array_start<base, scalar>(node); ::new(static_cast<void*>(node)) base(allocator, size, start); result.release(); return shared_ptr<T>(detail::sp_internal_constructor_tag(), reinterpret_cast<type*>(start), detail::shared_count(node)); } template<class T, class A> inline typename enable_if_<is_bounded_array<T>::value, shared_ptr<T> >::type allocate_shared(const A& allocator) { typedef typename remove_extent<T>::type type; typedef typename detail::sp_array_scalar<T>::type scalar; typedef typename detail::sp_bind_allocator<A, scalar>::type other; enum { size = detail::sp_array_count<T, scalar>::value }; typedef detail::sp_size_array_state<other, size> state; typedef detail::sp_array_base<state> base; detail::sp_array_result<other, base> result(allocator, size); detail::sp_counted_base* node = result.get(); scalar* start = detail::sp_array_start<base, scalar>(node); ::new(static_cast<void*>(node)) base(allocator, size, start); result.release(); return shared_ptr<T>(detail::sp_internal_constructor_tag(), reinterpret_cast<type*>(start), detail::shared_count(node)); } template<class T, class A> 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) { typedef typename remove_extent<T>::type type; typedef typename detail::sp_array_scalar<T>::type scalar; typedef typename detail::sp_bind_allocator<A, scalar>::type other; typedef detail::sp_array_state<other> state; typedef detail::sp_array_base<state> base; enum { total = detail::sp_array_count<type, scalar>::value }; std::size_t size = count * total; detail::sp_array_result<other, base> result(allocator, size); detail::sp_counted_base* node = result.get(); scalar* start = detail::sp_array_start<base, scalar>(node); ::new(static_cast<void*>(node)) base(allocator, size, reinterpret_cast<const scalar*>(&value), total, start); result.release(); return shared_ptr<T>(detail::sp_internal_constructor_tag(), reinterpret_cast<type*>(start), detail::shared_count(node)); } template<class T, class A> 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) { typedef typename remove_extent<T>::type type; typedef typename detail::sp_array_scalar<T>::type scalar; typedef typename detail::sp_bind_allocator<A, scalar>::type other; enum { size = detail::sp_array_count<T, scalar>::value }; typedef detail::sp_size_array_state<other, size> state; typedef detail::sp_array_base<state> base; detail::sp_array_result<other, base> result(allocator, size); detail::sp_counted_base* node = result.get(); scalar* start = detail::sp_array_start<base, scalar>(node); ::new(static_cast<void*>(node)) base(allocator, size, reinterpret_cast<const scalar*>(&value), detail::sp_array_count<type, scalar>::value, start); result.release(); return shared_ptr<T>(detail::sp_internal_constructor_tag(), reinterpret_cast<type*>(start), detail::shared_count(node)); } template<class T, class A> inline typename enable_if_<is_unbounded_array<T>::value, shared_ptr<T> >::type allocate_shared_noinit(const A& allocator, std::size_t count) { typedef typename remove_extent<T>::type type; typedef typename detail::sp_array_scalar<T>::type scalar; typedef typename detail::sp_bind_allocator<A, scalar>::type other; typedef detail::sp_array_state<other> state; typedef detail::sp_array_base<state, false> base; std::size_t size = count * detail::sp_array_count<type, scalar>::value; detail::sp_array_result<other, base> result(allocator, size); detail::sp_counted_base* node = result.get(); scalar* start = detail::sp_array_start<base, scalar>(node); ::new(static_cast<void*>(node)) base(detail::sp_default(), allocator, size, start); result.release(); return shared_ptr<T>(detail::sp_internal_constructor_tag(), reinterpret_cast<type*>(start), detail::shared_count(node)); } template<class T, class A> inline typename enable_if_<is_bounded_array<T>::value, shared_ptr<T> >::type allocate_shared_noinit(const A& allocator) { typedef typename remove_extent<T>::type type; typedef typename detail::sp_array_scalar<T>::type scalar; typedef typename detail::sp_bind_allocator<A, scalar>::type other; enum { size = detail::sp_array_count<T, scalar>::value }; typedef detail::sp_size_array_state<other, size> state; typedef detail::sp_array_base<state, false> base; detail::sp_array_result<other, base> result(allocator, size); detail::sp_counted_base* node = result.get(); scalar* start = detail::sp_array_start<base, scalar>(node); ::new(static_cast<void*>(node)) base(detail::sp_default(), allocator, size, start); result.release(); return shared_ptr<T>(detail::sp_internal_constructor_tag(), reinterpret_cast<type*>(start), detail::shared_count(node)); } } /* boost */ #endif