1 ////////////////////////////////////////////////////////////////////////////// 2 // 3 // (C) Copyright Ion Gaztanaga 2015-2015. Distributed under the Boost 4 // Software License, Version 1.0. (See accompanying file 5 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 // 7 // See http://www.boost.org/libs/container for documentation. 8 // 9 ////////////////////////////////////////////////////////////////////////////// 10 11 #ifndef BOOST_CONTAINER_PMR_RESOURCE_ADAPTOR_HPP 12 #define BOOST_CONTAINER_PMR_RESOURCE_ADAPTOR_HPP 13 14 #if defined (_MSC_VER) 15 # pragma once 16 #endif 17 18 #include <boost/container/detail/config_begin.hpp> 19 #include <boost/container/detail/workaround.hpp> 20 #include <boost/container/container_fwd.hpp> 21 22 #include <boost/container/pmr/memory_resource.hpp> 23 #include <boost/container/allocator_traits.hpp> 24 #include <boost/intrusive/detail/ebo_functor_holder.hpp> 25 #include <boost/move/utility_core.hpp> 26 #include <boost/move/detail/type_traits.hpp> 27 #include <boost/container/detail/std_fwd.hpp> 28 29 #include <cstring> 30 31 namespace boost { 32 namespace container { 33 34 namespace pmr_dtl { 35 36 template<class T> 37 struct max_allocator_alignment 38 { 39 static const std::size_t value = 1; 40 }; 41 42 template<class T> 43 struct max_allocator_alignment< ::boost::container::new_allocator<T> > 44 { 45 static const std::size_t value = boost::move_detail::alignment_of<boost::move_detail::max_align_t>::value; 46 }; 47 48 template<class T> 49 struct max_allocator_alignment< std::allocator<T> > 50 { 51 static const std::size_t value = boost::move_detail::alignment_of<boost::move_detail::max_align_t>::value; 52 }; 53 54 } //namespace pmr_dtl 55 56 namespace pmr { 57 58 //! An instance of resource_adaptor<Allocator> is an adaptor that wraps a memory_resource interface 59 //! around Allocator. In order that resource_adaptor<X<T>> and resource_adaptor<X<U>> are the same 60 //! type for any allocator template X and types T and U, resource_adaptor<Allocator> is rendered as 61 //! an alias to this class template such that Allocator is rebound to a char value type in every 62 //! specialization of the class template. The requirements on this class template are defined below. 63 //! In addition to the Allocator requirements, the parameter to resource_adaptor shall meet 64 //! the following additional requirements: 65 //! 66 //! - `typename allocator_traits<Allocator>:: pointer` shall be identical to 67 //! `typename allocator_traits<Allocator>:: value_type*`. 68 //! 69 //! - `typename allocator_traits<Allocator>:: const_pointer` shall be identical to 70 //! `typename allocator_traits<Allocator>:: value_type const*`. 71 //! 72 //! - `typename allocator_traits<Allocator>:: void_pointer` shall be identical to `void*`. 73 //! 74 //! - `typename allocator_traits<Allocator>:: const_void_pointer` shall be identical to `void const*`. 75 template <class Allocator> 76 class resource_adaptor_imp 77 : public memory_resource 78 #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED 79 , private ::boost::intrusive::detail::ebo_functor_holder<Allocator> 80 #endif 81 { 82 #ifdef BOOST_CONTAINER_DOXYGEN_INVOKED 83 Allocator m_alloc; 84 #else 85 BOOST_COPYABLE_AND_MOVABLE(resource_adaptor_imp) 86 typedef ::boost::intrusive::detail::ebo_functor_holder<Allocator> ebo_alloc_t; 87 void static_assert_if_not_char_allocator() const 88 { 89 //This class can only be used with allocators type char 90 BOOST_STATIC_ASSERT((boost::container::dtl::is_same<typename Allocator::value_type, char>::value)); 91 } 92 #endif 93 94 public: 95 typedef Allocator allocator_type; 96 97 //! <b>Effects</b>: Default constructs 98 //! m_alloc. resource_adaptor_imp()99 resource_adaptor_imp() 100 { this->static_assert_if_not_char_allocator(); } 101 102 //! <b>Effects</b>: Copy constructs 103 //! m_alloc. resource_adaptor_imp(const resource_adaptor_imp & other)104 resource_adaptor_imp(const resource_adaptor_imp &other) 105 : ebo_alloc_t(other.ebo_alloc_t::get()) 106 {} 107 108 //! <b>Effects</b>: Move constructs 109 //! m_alloc. resource_adaptor_imp(BOOST_RV_REF (resource_adaptor_imp)other)110 resource_adaptor_imp(BOOST_RV_REF(resource_adaptor_imp) other) 111 : ebo_alloc_t(::boost::move(other.get())) 112 {} 113 114 //! <b>Effects</b>: Initializes m_alloc with 115 //! a2. resource_adaptor_imp(const Allocator & a2)116 explicit resource_adaptor_imp(const Allocator& a2) 117 : ebo_alloc_t(a2) 118 { this->static_assert_if_not_char_allocator(); } 119 120 //! <b>Effects</b>: Initializes m_alloc with 121 //! a2. resource_adaptor_imp(BOOST_RV_REF (Allocator)a2)122 explicit resource_adaptor_imp(BOOST_RV_REF(Allocator) a2) 123 : ebo_alloc_t(::boost::move(a2)) 124 { this->static_assert_if_not_char_allocator(); } 125 126 //! <b>Effects</b>: Copy assigns 127 //! m_alloc. operator =(BOOST_COPY_ASSIGN_REF (resource_adaptor_imp)other)128 resource_adaptor_imp& operator=(BOOST_COPY_ASSIGN_REF(resource_adaptor_imp) other) 129 { this->ebo_alloc_t::get() = other.ebo_alloc_t::get(); return *this; } 130 131 //! <b>Effects</b>: Move assigns 132 //! m_alloc. operator =(BOOST_RV_REF (resource_adaptor_imp)other)133 resource_adaptor_imp& operator=(BOOST_RV_REF(resource_adaptor_imp) other) 134 { this->ebo_alloc_t::get() = ::boost::move(other.ebo_alloc_t::get()); return *this; } 135 136 //! <b>Effects</b>: Returns m_alloc. get_allocator()137 allocator_type &get_allocator() 138 { return this->ebo_alloc_t::get(); } 139 140 //! <b>Effects</b>: Returns m_alloc. get_allocator() const141 const allocator_type &get_allocator() const 142 { return this->ebo_alloc_t::get(); } 143 144 protected: 145 //! <b>Returns</b>: Allocated memory obtained by calling m_alloc.allocate. The size and alignment 146 //! of the allocated memory shall meet the requirements for a class derived from memory_resource. do_allocate(std::size_t bytes,std::size_t alignment)147 virtual void* do_allocate(std::size_t bytes, std::size_t alignment) 148 { 149 if (alignment <= priv_guaranteed_allocator_alignment()) 150 return this->ebo_alloc_t::get().allocate(bytes); 151 else 152 return this->priv_aligned_alloc(bytes, alignment); 153 } 154 155 //! <b>Requires</b>: p was previously allocated using A.allocate, where A == m_alloc, and not 156 //! subsequently deallocated. 157 //! 158 //! <b>Effects</b>: Returns memory to the allocator using m_alloc.deallocate(). do_deallocate(void * p,std::size_t bytes,std::size_t alignment)159 virtual void do_deallocate(void* p, std::size_t bytes, std::size_t alignment) 160 { 161 if (alignment <= priv_guaranteed_allocator_alignment()) 162 this->ebo_alloc_t::get().deallocate((char*)p, bytes); 163 else 164 this->priv_aligned_dealloc(p, bytes, alignment); 165 } 166 167 //! Let p be dynamic_cast<const resource_adaptor_imp*>(&other). 168 //! 169 //! <b>Returns</b>: false if p is null, otherwise the value of m_alloc == p->m_alloc. do_is_equal(const memory_resource & other) const170 virtual bool do_is_equal(const memory_resource& other) const BOOST_NOEXCEPT 171 { 172 const resource_adaptor_imp* p = dynamic_cast<const resource_adaptor_imp*>(&other); 173 return p && p->ebo_alloc_t::get() == this->ebo_alloc_t::get(); 174 } 175 176 private: priv_aligned_alloc(std::size_t bytes,std::size_t alignment)177 void * priv_aligned_alloc(std::size_t bytes, std::size_t alignment) 178 { 179 //Allocate space for requested bytes, plus alignment, plus bookeeping data 180 void *const p = this->ebo_alloc_t::get().allocate(bytes + priv_extra_bytes_for_overalignment(alignment)); 181 182 if (0 != p) { 183 //Obtain the aligned address after the bookeeping data 184 void *const aligned_ptr = (void*)(((std::size_t)p + priv_extra_bytes_for_overalignment(alignment)) & ~(alignment - 1)); 185 186 //Store bookeeping data. Use memcpy as the underlying memory might be unaligned for 187 //a pointer (e.g. 2 byte alignment in 32 bit, 4 byte alignment in 64 bit) 188 std::memcpy(priv_bookeeping_addr_from_aligned_ptr(aligned_ptr), &p, sizeof(p)); 189 return aligned_ptr; 190 } 191 return 0; 192 } 193 priv_aligned_dealloc(void * aligned_ptr,std::size_t bytes,std::size_t alignment)194 void priv_aligned_dealloc(void *aligned_ptr, std::size_t bytes, std::size_t alignment) 195 { 196 //Obtain bookeeping data 197 void *p; 198 std::memcpy(&p, priv_bookeeping_addr_from_aligned_ptr(aligned_ptr), sizeof(p)); 199 std::size_t s = bytes + priv_extra_bytes_for_overalignment(alignment); 200 this->ebo_alloc_t::get().deallocate((char*)p, s); 201 } 202 priv_bookeeping_addr_from_aligned_ptr(void * aligned_ptr)203 static BOOST_CONTAINER_FORCEINLINE void *priv_bookeeping_addr_from_aligned_ptr(void *aligned_ptr) 204 { 205 return reinterpret_cast<void*>(reinterpret_cast<std::size_t>(aligned_ptr) - sizeof(void*)); 206 } 207 priv_extra_bytes_for_overalignment(std::size_t alignment)208 BOOST_CONTAINER_FORCEINLINE static std::size_t priv_extra_bytes_for_overalignment(std::size_t alignment) 209 { 210 return alignment - 1 + sizeof(void*); 211 } 212 priv_guaranteed_allocator_alignment()213 BOOST_CONTAINER_FORCEINLINE static std::size_t priv_guaranteed_allocator_alignment() 214 { 215 return pmr_dtl::max_allocator_alignment<Allocator>::value; 216 } 217 }; 218 219 #if !defined(BOOST_NO_CXX11_TEMPLATE_ALIASES) || defined(BOOST_CONTAINER_DOXYGEN_INVOKED) 220 221 //! `resource_adaptor<Allocator>` is rendered as an alias to resource_adaptor_imp class template 222 //! such that Allocator is rebound to a char value type. 223 template <class Allocator> 224 using resource_adaptor = resource_adaptor_imp 225 <typename allocator_traits<Allocator>::template rebind_alloc<char> >; 226 227 #else 228 229 template <class Allocator> 230 class resource_adaptor 231 : public resource_adaptor_imp 232 <typename allocator_traits<Allocator>::template portable_rebind_alloc<char>::type> 233 { 234 typedef resource_adaptor_imp 235 <typename allocator_traits<Allocator>::template portable_rebind_alloc<char>::type> base_t; 236 237 BOOST_COPYABLE_AND_MOVABLE(resource_adaptor) 238 239 public: resource_adaptor()240 resource_adaptor() 241 : base_t() 242 {} 243 resource_adaptor(const resource_adaptor & other)244 resource_adaptor(const resource_adaptor &other) 245 : base_t(other) 246 {} 247 resource_adaptor(BOOST_RV_REF (resource_adaptor)other)248 resource_adaptor(BOOST_RV_REF(resource_adaptor) other) 249 : base_t(BOOST_MOVE_BASE(base_t, other)) 250 {} 251 resource_adaptor(const Allocator & a2)252 explicit resource_adaptor(const Allocator& a2) 253 : base_t(a2) 254 {} 255 resource_adaptor(BOOST_RV_REF (Allocator)a2)256 explicit resource_adaptor(BOOST_RV_REF(Allocator) a2) 257 : base_t(::boost::move(a2)) 258 {} 259 operator =(BOOST_COPY_ASSIGN_REF (resource_adaptor)other)260 resource_adaptor& operator=(BOOST_COPY_ASSIGN_REF(resource_adaptor) other) 261 { return static_cast<resource_adaptor&>(this->base_t::operator=(other)); } 262 operator =(BOOST_RV_REF (resource_adaptor)other)263 resource_adaptor& operator=(BOOST_RV_REF(resource_adaptor) other) 264 { return static_cast<resource_adaptor&>(this->base_t::operator=(BOOST_MOVE_BASE(base_t, other))); } 265 266 //get_allocator and protected functions are properly inherited 267 }; 268 269 #endif 270 271 } //namespace pmr { 272 } //namespace container { 273 } //namespace boost { 274 275 #include <boost/container/detail/config_end.hpp> 276 277 #endif //BOOST_CONTAINER_PMR_RESOURCE_ADAPTOR_HPP 278