1 ///////////////////////////////////////////////////////////////////////////// 2 // 3 // (C) Copyright Olaf Krzikalla 2004-2006. 4 // (C) Copyright Ion Gaztanaga 2006-2014 5 // 6 // Distributed under the Boost Software License, Version 1.0. 7 // (See accompanying file LICENSE_1_0.txt or copy at 8 // http://www.boost.org/LICENSE_1_0.txt) 9 // 10 // See http://www.boost.org/libs/intrusive for documentation. 11 // 12 ///////////////////////////////////////////////////////////////////////////// 13 14 #ifndef BOOST_INTRUSIVE_LINEAR_SLIST_ALGORITHMS_HPP 15 #define BOOST_INTRUSIVE_LINEAR_SLIST_ALGORITHMS_HPP 16 17 #include <boost/intrusive/detail/config_begin.hpp> 18 #include <boost/intrusive/intrusive_fwd.hpp> 19 #include <boost/intrusive/detail/common_slist_algorithms.hpp> 20 #include <boost/intrusive/detail/algo_type.hpp> 21 #include <cstddef> 22 #include <boost/intrusive/detail/twin.hpp> //for node_pair 23 24 #if defined(BOOST_HAS_PRAGMA_ONCE) 25 # pragma once 26 #endif 27 28 namespace boost { 29 namespace intrusive { 30 31 //! linear_slist_algorithms provides basic algorithms to manipulate nodes 32 //! forming a linear singly linked list. 33 //! 34 //! linear_slist_algorithms is configured with a NodeTraits class, which encapsulates the 35 //! information about the node to be manipulated. NodeTraits must support the 36 //! following interface: 37 //! 38 //! <b>Typedefs</b>: 39 //! 40 //! <tt>node</tt>: The type of the node that forms the linear list 41 //! 42 //! <tt>node_ptr</tt>: A pointer to a node 43 //! 44 //! <tt>const_node_ptr</tt>: A pointer to a const node 45 //! 46 //! <b>Static functions</b>: 47 //! 48 //! <tt>static node_ptr get_next(const_node_ptr n);</tt> 49 //! 50 //! <tt>static void set_next(node_ptr n, node_ptr next);</tt> 51 template<class NodeTraits> 52 class linear_slist_algorithms 53 /// @cond 54 : public detail::common_slist_algorithms<NodeTraits> 55 /// @endcond 56 { 57 /// @cond 58 typedef detail::common_slist_algorithms<NodeTraits> base_t; 59 /// @endcond 60 public: 61 typedef typename NodeTraits::node node; 62 typedef typename NodeTraits::node_ptr node_ptr; 63 typedef typename NodeTraits::const_node_ptr const_node_ptr; 64 typedef NodeTraits node_traits; 65 //A simple struct containing: 66 // 67 // typedef node_ptr type; 68 // node_ptr first; 69 // node_ptr second; 70 typedef twin<node_ptr> node_pair; 71 72 #if defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED) 73 74 //! <b>Effects</b>: Constructs an non-used list element, putting the next 75 //! pointer to null: 76 //! <tt>NodeTraits::get_next(this_node) == node_ptr()</tt> 77 //! 78 //! <b>Complexity</b>: Constant 79 //! 80 //! <b>Throws</b>: Nothing. 81 static void init(const node_ptr & this_node); 82 83 //! <b>Requires</b>: this_node must be in a circular list or be an empty circular list. 84 //! 85 //! <b>Effects</b>: Returns true is "this_node" is the only node of a circular list: 86 //! or it's a not inserted node: 87 //! <tt>return node_ptr() == NodeTraits::get_next(this_node) || NodeTraits::get_next(this_node) == this_node</tt> 88 //! 89 //! <b>Complexity</b>: Constant 90 //! 91 //! <b>Throws</b>: Nothing. 92 static bool unique(const_node_ptr this_node); 93 94 //! <b>Effects</b>: Returns true is "this_node" has the same state as if 95 //! it was inited using "init(node_ptr)" 96 //! 97 //! <b>Complexity</b>: Constant 98 //! 99 //! <b>Throws</b>: Nothing. 100 static bool inited(const_node_ptr this_node); 101 102 //! <b>Requires</b>: prev_node must be in a circular list or be an empty circular list. 103 //! 104 //! <b>Effects</b>: Unlinks the next node of prev_node from the circular list. 105 //! 106 //! <b>Complexity</b>: Constant 107 //! 108 //! <b>Throws</b>: Nothing. 109 static void unlink_after(const node_ptr & prev_node); 110 111 //! <b>Requires</b>: prev_node and last_node must be in a circular list 112 //! or be an empty circular list. 113 //! 114 //! <b>Effects</b>: Unlinks the range (prev_node, last_node) from the linear list. 115 //! 116 //! <b>Complexity</b>: Constant 117 //! 118 //! <b>Throws</b>: Nothing. 119 static void unlink_after(const node_ptr & prev_node, const node_ptr & last_node); 120 121 //! <b>Requires</b>: prev_node must be a node of a linear list. 122 //! 123 //! <b>Effects</b>: Links this_node after prev_node in the linear list. 124 //! 125 //! <b>Complexity</b>: Constant 126 //! 127 //! <b>Throws</b>: Nothing. 128 static void link_after(const node_ptr & prev_node, const node_ptr & this_node); 129 130 //! <b>Requires</b>: b and e must be nodes of the same linear list or an empty range. 131 //! and p must be a node of a different linear list. 132 //! 133 //! <b>Effects</b>: Removes the nodes from (b, e] range from their linear list and inserts 134 //! them after p in p's linear list. 135 //! 136 //! <b>Complexity</b>: Constant 137 //! 138 //! <b>Throws</b>: Nothing. 139 static void transfer_after(const node_ptr & p, const node_ptr & b, const node_ptr & e); 140 141 #endif //#if defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED) 142 143 //! <b>Effects</b>: Constructs an empty list, making this_node the only 144 //! node of the circular list: 145 //! <tt>NodeTraits::get_next(this_node) == this_node</tt>. 146 //! 147 //! <b>Complexity</b>: Constant 148 //! 149 //! <b>Throws</b>: Nothing. init_header(const node_ptr & this_node)150 BOOST_INTRUSIVE_FORCEINLINE static void init_header(const node_ptr & this_node) 151 { NodeTraits::set_next(this_node, node_ptr ()); } 152 153 //! <b>Requires</b>: this_node and prev_init_node must be in the same linear list. 154 //! 155 //! <b>Effects</b>: Returns the previous node of this_node in the linear list starting. 156 //! the search from prev_init_node. The first node checked for equality 157 //! is NodeTraits::get_next(prev_init_node). 158 //! 159 //! <b>Complexity</b>: Linear to the number of elements between prev_init_node and this_node. 160 //! 161 //! <b>Throws</b>: Nothing. get_previous_node(const node_ptr & prev_init_node,const node_ptr & this_node)162 BOOST_INTRUSIVE_FORCEINLINE static node_ptr get_previous_node(const node_ptr & prev_init_node, const node_ptr & this_node) 163 { return base_t::get_previous_node(prev_init_node, this_node); } 164 165 //! <b>Requires</b>: this_node must be in a linear list or be an empty linear list. 166 //! 167 //! <b>Effects</b>: Returns the number of nodes in a linear list. If the linear list 168 //! is empty, returns 1. 169 //! 170 //! <b>Complexity</b>: Linear 171 //! 172 //! <b>Throws</b>: Nothing. count(const const_node_ptr & this_node)173 static std::size_t count(const const_node_ptr & this_node) 174 { 175 std::size_t result = 0; 176 const_node_ptr p = this_node; 177 do{ 178 p = NodeTraits::get_next(p); 179 ++result; 180 } while (p); 181 return result; 182 } 183 184 //! <b>Requires</b>: this_node and other_node must be nodes inserted 185 //! in linear lists or be empty linear lists. 186 //! 187 //! <b>Effects</b>: Moves all the nodes previously chained after this_node after other_node 188 //! and vice-versa. 189 //! 190 //! <b>Complexity</b>: Constant 191 //! 192 //! <b>Throws</b>: Nothing. swap_trailing_nodes(node_ptr this_node,node_ptr other_node)193 static void swap_trailing_nodes(node_ptr this_node, node_ptr other_node) 194 { 195 node_ptr this_nxt = NodeTraits::get_next(this_node); 196 node_ptr other_nxt = NodeTraits::get_next(other_node); 197 NodeTraits::set_next(this_node, other_nxt); 198 NodeTraits::set_next(other_node, this_nxt); 199 } 200 201 //! <b>Effects</b>: Reverses the order of elements in the list. 202 //! 203 //! <b>Returns</b>: The new first node of the list. 204 //! 205 //! <b>Throws</b>: Nothing. 206 //! 207 //! <b>Complexity</b>: This function is linear to the contained elements. reverse(node_ptr p)208 static node_ptr reverse(node_ptr p) 209 { 210 if(!p) return node_ptr(); 211 node_ptr i = NodeTraits::get_next(p); 212 node_ptr first(p); 213 while(i){ 214 node_ptr nxti(NodeTraits::get_next(i)); 215 base_t::unlink_after(p); 216 NodeTraits::set_next(i, first); 217 first = i; 218 i = nxti; 219 } 220 return first; 221 } 222 223 //! <b>Effects</b>: Moves the first n nodes starting at p to the end of the list. 224 //! 225 //! <b>Returns</b>: A pair containing the new first and last node of the list or 226 //! if there has been any movement, a null pair if n leads to no movement. 227 //! 228 //! <b>Throws</b>: Nothing. 229 //! 230 //! <b>Complexity</b>: Linear to the number of elements plus the number moved positions. move_first_n_backwards(node_ptr p,std::size_t n)231 static node_pair move_first_n_backwards(node_ptr p, std::size_t n) 232 { 233 node_pair ret; 234 //Null shift, or count() == 0 or 1, nothing to do 235 if(!n || !p || !NodeTraits::get_next(p)){ 236 return ret; 237 } 238 239 node_ptr first = p; 240 bool end_found = false; 241 node_ptr new_last = node_ptr(); 242 node_ptr old_last = node_ptr(); 243 244 //Now find the new last node according to the shift count. 245 //If we find 0 before finding the new last node 246 //unlink p, shortcut the search now that we know the size of the list 247 //and continue. 248 for(std::size_t i = 1; i <= n; ++i){ 249 new_last = first; 250 first = NodeTraits::get_next(first); 251 if(first == node_ptr()){ 252 //Shortcut the shift with the modulo of the size of the list 253 n %= i; 254 if(!n) return ret; 255 old_last = new_last; 256 i = 0; 257 //Unlink p and continue the new first node search 258 first = p; 259 //unlink_after(new_last); 260 end_found = true; 261 } 262 } 263 264 //If the p has not been found in the previous loop, find it 265 //starting in the new first node and unlink it 266 if(!end_found){ 267 old_last = base_t::get_previous_node(first, node_ptr()); 268 } 269 270 //Now link p after the new last node 271 NodeTraits::set_next(old_last, p); 272 NodeTraits::set_next(new_last, node_ptr()); 273 ret.first = first; 274 ret.second = new_last; 275 return ret; 276 } 277 278 //! <b>Effects</b>: Moves the first n nodes starting at p to the beginning of the list. 279 //! 280 //! <b>Returns</b>: A pair containing the new first and last node of the list or 281 //! if there has been any movement, a null pair if n leads to no movement. 282 //! 283 //! <b>Throws</b>: Nothing. 284 //! 285 //! <b>Complexity</b>: Linear to the number of elements plus the number moved positions. move_first_n_forward(node_ptr p,std::size_t n)286 static node_pair move_first_n_forward(node_ptr p, std::size_t n) 287 { 288 node_pair ret; 289 //Null shift, or count() == 0 or 1, nothing to do 290 if(!n || !p || !NodeTraits::get_next(p)) 291 return ret; 292 293 node_ptr first = p; 294 295 //Iterate until p is found to know where the current last node is. 296 //If the shift count is less than the size of the list, we can also obtain 297 //the position of the new last node after the shift. 298 node_ptr old_last(first), next_to_it, new_last(p); 299 std::size_t distance = 1; 300 while(!!(next_to_it = node_traits::get_next(old_last))){ 301 if(distance++ > n) 302 new_last = node_traits::get_next(new_last); 303 old_last = next_to_it; 304 } 305 //If the shift was bigger or equal than the size, obtain the equivalent 306 //forward shifts and find the new last node. 307 if(distance <= n){ 308 //Now find the equivalent forward shifts. 309 //Shortcut the shift with the modulo of the size of the list 310 std::size_t new_before_last_pos = (distance - (n % distance))% distance; 311 //If the shift is a multiple of the size there is nothing to do 312 if(!new_before_last_pos) 313 return ret; 314 315 for( new_last = p 316 ; --new_before_last_pos 317 ; new_last = node_traits::get_next(new_last)){ 318 //empty 319 } 320 } 321 322 //Get the first new node 323 node_ptr new_first(node_traits::get_next(new_last)); 324 //Now put the old beginning after the old end 325 NodeTraits::set_next(old_last, p); 326 NodeTraits::set_next(new_last, node_ptr()); 327 ret.first = new_first; 328 ret.second = new_last; 329 return ret; 330 } 331 }; 332 333 /// @cond 334 335 template<class NodeTraits> 336 struct get_algo<LinearSListAlgorithms, NodeTraits> 337 { 338 typedef linear_slist_algorithms<NodeTraits> type; 339 }; 340 341 /// @endcond 342 343 } //namespace intrusive 344 } //namespace boost 345 346 #include <boost/intrusive/detail/config_end.hpp> 347 348 #endif //BOOST_INTRUSIVE_LINEAR_SLIST_ALGORITHMS_HPP 349