1 /* Copyright 2003-2020 Joaquin M Lopez Munoz.
2  * Distributed under the Boost Software License, Version 1.0.
3  * (See accompanying file LICENSE_1_0.txt or copy at
4  * http://www.boost.org/LICENSE_1_0.txt)
5  *
6  * See http://www.boost.org/libs/multi_index for library home page.
7  */
8 
9 #ifndef BOOST_MULTI_INDEX_DETAIL_NODE_HANDLE_HPP
10 #define BOOST_MULTI_INDEX_DETAIL_NODE_HANDLE_HPP
11 
12 #if defined(_MSC_VER)
13 #pragma once
14 #endif
15 
16 #include <boost/config.hpp> /* keep it first to prevent nasty warns in MSVC */
17 #include <algorithm>
18 #include <boost/core/addressof.hpp>
19 #include <boost/detail/workaround.hpp>
20 #include <boost/move/core.hpp>
21 #include <boost/move/utility_core.hpp>
22 #include <boost/multi_index_container_fwd.hpp>
23 #include <boost/multi_index/detail/allocator_traits.hpp>
24 #include <boost/type_traits/aligned_storage.hpp>
25 #include <boost/type_traits/alignment_of.hpp>
26 #include <new>
27 
28 namespace boost{
29 
30 namespace multi_index{
31 
32 namespace detail{
33 
34 /* Node handle template class following [container.node] specs.
35  */
36 
37 #include <boost/multi_index/detail/define_if_constexpr_macro.hpp>
38 
39 template<typename Node,typename Allocator>
40 class node_handle
41 {
42 public:
43   typedef typename Node::value_type        value_type;
44   typedef Allocator                        allocator_type;
45 
46 private:
47   typedef allocator_traits<allocator_type> alloc_traits;
48 
49 public:
node_handle()50   node_handle()BOOST_NOEXCEPT:node(0){}
51 
node_handle(BOOST_RV_REF (node_handle)x)52   node_handle(BOOST_RV_REF(node_handle) x)BOOST_NOEXCEPT:node(x.node)
53   {
54     if(!x.empty()){
55       move_construct_allocator(boost::move(x));
56       x.destroy_allocator();
57       x.node=0;
58     }
59   }
60 
~node_handle()61   ~node_handle()
62   {
63     if(!empty()){
64       delete_node();
65       destroy_allocator();
66     }
67   }
68 
operator =(BOOST_RV_REF (node_handle)x)69   node_handle& operator=(BOOST_RV_REF(node_handle) x)
70   {
71     if(this!=&x){
72       if(!empty()){
73         delete_node();
74         if(!x.empty()){
75           BOOST_MULTI_INDEX_IF_CONSTEXPR(
76             alloc_traits::propagate_on_container_move_assignment::value){
77             move_assign_allocator(boost::move(x));
78           }
79           x.destroy_allocator();
80         }
81         else{
82           destroy_allocator();
83         }
84       }
85       else if(!x.empty()){
86         move_construct_allocator(boost::move(x));
87         x.destroy_allocator();
88       }
89       node=x.node;
90       x.node=0;
91     }
92     return *this;
93   }
94 
value() const95   value_type& value()const{return node->value();}
get_allocator() const96   allocator_type get_allocator()const{return *allocator_ptr();}
97 
98 #if !defined(BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS)
99   explicit
100 #endif
operator bool() const101   operator bool()const BOOST_NOEXCEPT{return (node!=0);}
102 
103 #if BOOST_WORKAROUND(BOOST_GCC_VERSION,>=70000)&&__cplusplus<201103L
104   /* https://github.com/boostorg/config/issues/336 */
105 #else
106   BOOST_ATTRIBUTE_NODISCARD
107 #endif
empty() const108   bool empty()const BOOST_NOEXCEPT{return (node==0);}
109 
swap(node_handle & x)110   void swap(node_handle& x)
111     BOOST_NOEXCEPT_IF(
112       alloc_traits::propagate_on_container_swap::value||
113       alloc_traits::is_always_equal::value)
114   {
115     if(!empty()){
116       if(!x.empty()){
117         BOOST_MULTI_INDEX_IF_CONSTEXPR(
118           alloc_traits::propagate_on_container_swap::value){
119           using std::swap;
120           swap(*allocator_ptr(),*x.allocator_ptr());
121         }
122       }
123       else{
124         x.move_construct_allocator(boost::move(*this));
125         destroy_allocator();
126       }
127     }
128     else if(!x.empty()){
129       move_construct_allocator(boost::move(x));
130       x.destroy_allocator();
131     }
132     std::swap(node,x.node);
133   }
134 
swap(node_handle & x,node_handle & y)135   friend void swap(node_handle& x,node_handle& y)
136     BOOST_NOEXCEPT_IF(noexcept(x.swap(y)))
137   {
138     x.swap(y);
139   }
140 
141 private:
142   BOOST_MOVABLE_BUT_NOT_COPYABLE(node_handle)
143 
144   template <typename,typename,typename>
145   friend class boost::multi_index::multi_index_container;
146 
node_handle(Node * node_,const allocator_type & al)147   node_handle(Node* node_,const allocator_type& al):node(node_)
148   {
149     ::new (static_cast<void*>(allocator_ptr())) allocator_type(al);
150   }
151 
release_node()152   void release_node()
153   {
154     if(!empty()){
155       node=0;
156       destroy_allocator();
157     }
158   }
159 
160 #include <boost/multi_index/detail/ignore_wstrict_aliasing.hpp>
161 
allocator_ptr() const162   const allocator_type* allocator_ptr()const
163   {
164     return reinterpret_cast<const allocator_type*>(&space);
165   }
166 
allocator_ptr()167   allocator_type* allocator_ptr()
168   {
169     return reinterpret_cast<allocator_type*>(&space);
170   }
171 
172 #include <boost/multi_index/detail/restore_wstrict_aliasing.hpp>
173 
move_construct_allocator(BOOST_RV_REF (node_handle)x)174   void move_construct_allocator(BOOST_RV_REF(node_handle) x)
175   {
176     ::new (static_cast<void*>(allocator_ptr()))
177       allocator_type(boost::move(*x.allocator_ptr()));
178   }
179 
move_assign_allocator(BOOST_RV_REF (node_handle)x)180   void move_assign_allocator(BOOST_RV_REF(node_handle) x)
181   {
182     *allocator_ptr()=boost::move(*x.allocator_ptr());
183   }
184 
destroy_allocator()185   void destroy_allocator(){allocator_ptr()->~allocator_type();}
186 
delete_node()187   void delete_node()
188   {
189     typedef typename rebind_alloc_for<
190       allocator_type,Node
191     >::type                                          node_allocator;
192     typedef detail::allocator_traits<node_allocator> node_alloc_traits;
193     typedef typename node_alloc_traits::pointer      node_pointer;
194 
195     alloc_traits::destroy(*allocator_ptr(),boost::addressof(node->value()));
196     node_allocator nal(*allocator_ptr());
197     node_alloc_traits::deallocate(nal,static_cast<node_pointer>(node),1);
198   }
199 
200   Node*                                 node;
201   typename aligned_storage<
202     sizeof(allocator_type),
203     alignment_of<allocator_type>::value
204   >::type                               space;
205 };
206 
207 #include <boost/multi_index/detail/undef_if_constexpr_macro.hpp>
208 
209 /* node handle insert return type template class following
210  * [container.insert.return] specs.
211  */
212 
213 template<typename Iterator,typename NodeHandle>
214 struct insert_return_type
215 {
insert_return_typeboost::multi_index::detail::insert_return_type216   insert_return_type(
217     Iterator position_,bool inserted_,BOOST_RV_REF(NodeHandle) node_):
218     position(position_),inserted(inserted_),node(boost::move(node_)){}
insert_return_typeboost::multi_index::detail::insert_return_type219   insert_return_type(BOOST_RV_REF(insert_return_type) x):
220     position(x.position),inserted(x.inserted),node(boost::move(x.node)){}
221 
operator =boost::multi_index::detail::insert_return_type222   insert_return_type& operator=(BOOST_RV_REF(insert_return_type) x)
223   {
224     position=x.position;
225     inserted=x.inserted;
226     node=boost::move(x.node);
227     return *this;
228   }
229 
230   Iterator   position;
231   bool       inserted;
232   NodeHandle node;
233 
234 private:
235   BOOST_MOVABLE_BUT_NOT_COPYABLE(insert_return_type)
236 };
237 
238 } /* namespace multi_index::detail */
239 
240 } /* namespace multi_index */
241 
242 } /* namespace boost */
243 
244 #endif
245