resource_adaptor.hpp 9.7 KB

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