• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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