circular_slist_algorithms.hpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  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. #ifndef BOOST_INTRUSIVE_CIRCULAR_SLIST_ALGORITHMS_HPP
  14. #define BOOST_INTRUSIVE_CIRCULAR_SLIST_ALGORITHMS_HPP
  15. #include <cstddef>
  16. #include <boost/intrusive/detail/config_begin.hpp>
  17. #include <boost/intrusive/intrusive_fwd.hpp>
  18. #include <boost/intrusive/detail/common_slist_algorithms.hpp>
  19. #include <boost/intrusive/detail/uncast.hpp>
  20. #include <boost/intrusive/detail/algo_type.hpp>
  21. #if defined(BOOST_HAS_PRAGMA_ONCE)
  22. # pragma once
  23. #endif
  24. namespace boost {
  25. namespace intrusive {
  26. //! circular_slist_algorithms provides basic algorithms to manipulate nodes
  27. //! forming a circular singly linked list. An empty circular list is formed by a node
  28. //! whose pointer to the next node points to itself.
  29. //!
  30. //! circular_slist_algorithms is configured with a NodeTraits class, which encapsulates the
  31. //! information about the node to be manipulated. NodeTraits must support the
  32. //! following interface:
  33. //!
  34. //! <b>Typedefs</b>:
  35. //!
  36. //! <tt>node</tt>: The type of the node that forms the circular list
  37. //!
  38. //! <tt>node_ptr</tt>: A pointer to a node
  39. //!
  40. //! <tt>const_node_ptr</tt>: A pointer to a const node
  41. //!
  42. //! <b>Static functions</b>:
  43. //!
  44. //! <tt>static node_ptr get_next(const_node_ptr n);</tt>
  45. //!
  46. //! <tt>static void set_next(node_ptr n, node_ptr next);</tt>
  47. template<class NodeTraits>
  48. class circular_slist_algorithms
  49. /// @cond
  50. : public detail::common_slist_algorithms<NodeTraits>
  51. /// @endcond
  52. {
  53. /// @cond
  54. typedef detail::common_slist_algorithms<NodeTraits> base_t;
  55. /// @endcond
  56. public:
  57. typedef typename NodeTraits::node node;
  58. typedef typename NodeTraits::node_ptr node_ptr;
  59. typedef typename NodeTraits::const_node_ptr const_node_ptr;
  60. typedef NodeTraits node_traits;
  61. #if defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED)
  62. //! <b>Effects</b>: Constructs an non-used list element, putting the next
  63. //! pointer to null:
  64. //! <tt>NodeTraits::get_next(this_node) == node_ptr()</tt>
  65. //!
  66. //! <b>Complexity</b>: Constant
  67. //!
  68. //! <b>Throws</b>: Nothing.
  69. static void init(node_ptr this_node) BOOST_NOEXCEPT;
  70. //! <b>Requires</b>: this_node must be in a circular list or be an empty circular list.
  71. //!
  72. //! <b>Effects</b>: Returns true is "this_node" is the only node of a circular list:
  73. //! or it's a not inserted node:
  74. //! <tt>return node_ptr() == NodeTraits::get_next(this_node) || NodeTraits::get_next(this_node) == this_node</tt>
  75. //!
  76. //! <b>Complexity</b>: Constant
  77. //!
  78. //! <b>Throws</b>: Nothing.
  79. static bool unique(const_node_ptr this_node) BOOST_NOEXCEPT;
  80. //! <b>Effects</b>: Returns true is "this_node" has the same state as
  81. //! if it was inited using "init(node_ptr)"
  82. //!
  83. //! <b>Complexity</b>: Constant
  84. //!
  85. //! <b>Throws</b>: Nothing.
  86. static bool inited(const_node_ptr this_node) BOOST_NOEXCEPT;
  87. //! <b>Requires</b>: prev_node must be in a circular list or be an empty circular list.
  88. //!
  89. //! <b>Effects</b>: Unlinks the next node of prev_node from the circular list.
  90. //!
  91. //! <b>Complexity</b>: Constant
  92. //!
  93. //! <b>Throws</b>: Nothing.
  94. static void unlink_after(node_ptr prev_node) BOOST_NOEXCEPT;
  95. //! <b>Requires</b>: prev_node and last_node must be in a circular list
  96. //! or be an empty circular list.
  97. //!
  98. //! <b>Effects</b>: Unlinks the range (prev_node, last_node) from the circular list.
  99. //!
  100. //! <b>Complexity</b>: Constant
  101. //!
  102. //! <b>Throws</b>: Nothing.
  103. static void unlink_after(node_ptr prev_node, node_ptr last_node) BOOST_NOEXCEPT;
  104. //! <b>Requires</b>: prev_node must be a node of a circular list.
  105. //!
  106. //! <b>Effects</b>: Links this_node after prev_node in the circular list.
  107. //!
  108. //! <b>Complexity</b>: Constant
  109. //!
  110. //! <b>Throws</b>: Nothing.
  111. static void link_after(node_ptr prev_node, node_ptr this_node) BOOST_NOEXCEPT;
  112. //! <b>Requires</b>: b and e must be nodes of the same circular list or an empty range.
  113. //! and p must be a node of a different circular list.
  114. //!
  115. //! <b>Effects</b>: Removes the nodes from (b, e] range from their circular list and inserts
  116. //! them after p in p's circular list.
  117. //!
  118. //! <b>Complexity</b>: Constant
  119. //!
  120. //! <b>Throws</b>: Nothing.
  121. static void transfer_after(node_ptr p, node_ptr b, node_ptr e) BOOST_NOEXCEPT;
  122. #else
  123. using base_t::transfer_after;
  124. #endif //#if defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED)
  125. //! <b>Effects</b>: Constructs an empty list, making this_node the only
  126. //! node of the circular list:
  127. //! <tt>NodeTraits::get_next(this_node) == this_node</tt>.
  128. //!
  129. //! <b>Complexity</b>: Constant
  130. //!
  131. //! <b>Throws</b>: Nothing.
  132. inline static void init_header(node_ptr this_node) BOOST_NOEXCEPT
  133. { NodeTraits::set_next(this_node, this_node); }
  134. //! <b>Requires</b>: 'p' is the first node of a list.
  135. //!
  136. //! <b>Effects</b>: Returns a pointer to a node that represents the "end" (one past end) node
  137. //!
  138. //! <b>Complexity</b>: Constant time.
  139. //!
  140. //! <b>Throws</b>: Nothing.
  141. inline static node_ptr end_node(const_node_ptr p) BOOST_NOEXCEPT
  142. { return detail::uncast(p); }
  143. //! <b>Effects</b>: Returns true if this_node_points to an empty list.
  144. //!
  145. //! <b>Complexity</b>: Constant
  146. //!
  147. //! <b>Throws</b>: Nothing.
  148. inline static bool is_empty(const_node_ptr this_node) BOOST_NOEXCEPT
  149. { return NodeTraits::get_next(this_node) == this_node; }
  150. //! <b>Effects</b>: Returns true if this_node points to a sentinel node.
  151. //!
  152. //! <b>Complexity</b>: Constant
  153. //!
  154. //! <b>Throws</b>: Nothing.
  155. inline static bool is_sentinel(const_node_ptr this_node) BOOST_NOEXCEPT
  156. { return NodeTraits::get_next(this_node) == node_ptr(); }
  157. //! <b>Effects</b>: Marks this node as a "sentinel" node, a special state that is different from "empty",
  158. //! that can be used to mark a special state of the list
  159. //!
  160. //! <b>Complexity</b>: Constant
  161. //!
  162. //! <b>Throws</b>: Nothing.
  163. inline static void set_sentinel(node_ptr this_node) BOOST_NOEXCEPT
  164. { NodeTraits::set_next(this_node, node_ptr()); }
  165. //! <b>Requires</b>: this_node and prev_init_node must be in the same circular list.
  166. //!
  167. //! <b>Effects</b>: Returns the previous node of this_node in the circular list starting.
  168. //! the search from prev_init_node. The first node checked for equality
  169. //! is NodeTraits::get_next(prev_init_node).
  170. //!
  171. //! <b>Complexity</b>: Linear to the number of elements between prev_init_node and this_node.
  172. //!
  173. //! <b>Throws</b>: Nothing.
  174. inline static node_ptr get_previous_node(node_ptr prev_init_node, node_ptr this_node) BOOST_NOEXCEPT
  175. { return base_t::get_previous_node(prev_init_node, this_node); }
  176. //! <b>Requires</b>: this_node must be in a circular list or be an empty circular list.
  177. //!
  178. //! <b>Effects</b>: Returns the previous node of this_node in the circular list.
  179. //!
  180. //! <b>Complexity</b>: Linear to the number of elements in the circular list.
  181. //!
  182. //! <b>Throws</b>: Nothing.
  183. inline static node_ptr get_previous_node(node_ptr this_node) BOOST_NOEXCEPT
  184. { return base_t::get_previous_node(this_node, this_node); }
  185. //! <b>Requires</b>: this_node must be in a circular list or be an empty circular list.
  186. //!
  187. //! <b>Effects</b>: Returns the previous node of the previous node of this_node in the circular list.
  188. //!
  189. //! <b>Complexity</b>: Linear to the number of elements in the circular list.
  190. //!
  191. //! <b>Throws</b>: Nothing.
  192. inline static node_ptr get_previous_previous_node(node_ptr this_node) BOOST_NOEXCEPT
  193. { return get_previous_previous_node(this_node, this_node); }
  194. //! <b>Requires</b>: this_node and p must be in the same circular list.
  195. //!
  196. //! <b>Effects</b>: Returns the previous node of the previous node of this_node in the
  197. //! circular list starting. the search from p. The first node checked
  198. //! for equality is NodeTraits::get_next((NodeTraits::get_next(p)).
  199. //!
  200. //! <b>Complexity</b>: Linear to the number of elements in the circular list.
  201. //!
  202. //! <b>Throws</b>: Nothing.
  203. static node_ptr get_previous_previous_node(node_ptr p, node_ptr this_node) BOOST_NOEXCEPT
  204. {
  205. node_ptr p_next = NodeTraits::get_next(p);
  206. node_ptr p_next_next = NodeTraits::get_next(p_next);
  207. while (this_node != p_next_next){
  208. p = p_next;
  209. p_next = p_next_next;
  210. p_next_next = NodeTraits::get_next(p_next);
  211. }
  212. return p;
  213. }
  214. //! <b>Requires</b>: this_node must be in a circular list or be an empty circular list.
  215. //!
  216. //! <b>Effects</b>: Returns the number of nodes in a circular list. If the circular list
  217. //! is empty, returns 1.
  218. //!
  219. //! <b>Complexity</b>: Linear
  220. //!
  221. //! <b>Throws</b>: Nothing.
  222. static std::size_t count(const_node_ptr this_node) BOOST_NOEXCEPT
  223. {
  224. std::size_t result = 0;
  225. const_node_ptr p = this_node;
  226. do{
  227. p = NodeTraits::get_next(p);
  228. ++result;
  229. } while (p != this_node);
  230. return result;
  231. }
  232. //! <b>Requires</b>: this_node must be in a circular list, be an empty circular list or be inited.
  233. //!
  234. //! <b>Effects</b>: Unlinks the node from the circular list.
  235. //!
  236. //! <b>Complexity</b>: Linear to the number of elements in the circular list
  237. //!
  238. //! <b>Throws</b>: Nothing.
  239. static void unlink(node_ptr this_node) BOOST_NOEXCEPT
  240. {
  241. if(NodeTraits::get_next(this_node))
  242. base_t::unlink_after(get_previous_node(this_node));
  243. }
  244. //! <b>Requires</b>: nxt_node must be a node of a circular list.
  245. //!
  246. //! <b>Effects</b>: Links this_node before nxt_node in the circular list.
  247. //!
  248. //! <b>Complexity</b>: Linear to the number of elements in the circular list.
  249. //!
  250. //! <b>Throws</b>: Nothing.
  251. inline static void link_before (node_ptr nxt_node, node_ptr this_node) BOOST_NOEXCEPT
  252. { base_t::link_after(get_previous_node(nxt_node), this_node); }
  253. //! <b>Requires</b>: this_node and other_node must be nodes inserted
  254. //! in circular lists or be empty circular lists.
  255. //!
  256. //! <b>Effects</b>: Swaps the position of the nodes: this_node is inserted in
  257. //! other_nodes position in the second circular list and the other_node is inserted
  258. //! in this_node's position in the first circular list.
  259. //!
  260. //! <b>Complexity</b>: Linear to number of elements of both lists
  261. //!
  262. //! <b>Throws</b>: Nothing.
  263. static void swap_nodes(node_ptr this_node, node_ptr other_node) BOOST_NOEXCEPT
  264. {
  265. if (other_node == this_node)
  266. return;
  267. const node_ptr this_next = NodeTraits::get_next(this_node);
  268. const node_ptr other_next = NodeTraits::get_next(other_node);
  269. const bool this_null = !this_next;
  270. const bool other_null = !other_next;
  271. const bool this_empty = this_next == this_node;
  272. const bool other_empty = other_next == other_node;
  273. if(!(other_null || other_empty)){
  274. NodeTraits::set_next(this_next == other_node ? other_node : get_previous_node(other_node), this_node );
  275. }
  276. if(!(this_null | this_empty)){
  277. NodeTraits::set_next(other_next == this_node ? this_node : get_previous_node(this_node), other_node );
  278. }
  279. NodeTraits::set_next(this_node, other_empty ? this_node : (other_next == this_node ? other_node : other_next) );
  280. NodeTraits::set_next(other_node, this_empty ? other_node : (this_next == other_node ? this_node : this_next ) );
  281. }
  282. //! <b>Effects</b>: Reverses the order of elements in the list.
  283. //!
  284. //! <b>Throws</b>: Nothing.
  285. //!
  286. //! <b>Complexity</b>: This function is linear to the contained elements.
  287. static void reverse(node_ptr p) BOOST_NOEXCEPT
  288. {
  289. node_ptr i = NodeTraits::get_next(p), e(p);
  290. for (;;) {
  291. node_ptr nxt(NodeTraits::get_next(i));
  292. if (nxt == e)
  293. break;
  294. base_t::transfer_after(e, i, nxt);
  295. }
  296. }
  297. //! <b>Effects</b>: Moves the node p n positions towards the end of the list.
  298. //!
  299. //! <b>Returns</b>: The previous node of p after the function if there has been any movement,
  300. //! Null if n leads to no movement.
  301. //!
  302. //! <b>Throws</b>: Nothing.
  303. //!
  304. //! <b>Complexity</b>: Linear to the number of elements plus the number moved positions.
  305. static node_ptr move_backwards(node_ptr p, std::size_t n) BOOST_NOEXCEPT
  306. {
  307. //Null shift, nothing to do
  308. if(!n) return node_ptr();
  309. node_ptr first = NodeTraits::get_next(p);
  310. //count() == 1 or 2, nothing to do
  311. if(NodeTraits::get_next(first) == p)
  312. return node_ptr();
  313. bool end_found = false;
  314. node_ptr new_last = node_ptr();
  315. //Now find the new last node according to the shift count.
  316. //If we find p before finding the new last node
  317. //unlink p, shortcut the search now that we know the size of the list
  318. //and continue.
  319. for(std::size_t i = 1; i <= n; ++i){
  320. new_last = first;
  321. first = NodeTraits::get_next(first);
  322. if(first == p){
  323. //Shortcut the shift with the modulo of the size of the list
  324. n %= i;
  325. if(!n)
  326. return node_ptr();
  327. i = 0;
  328. //Unlink p and continue the new first node search
  329. first = NodeTraits::get_next(p);
  330. base_t::unlink_after(new_last);
  331. end_found = true;
  332. }
  333. }
  334. //If the p has not been found in the previous loop, find it
  335. //starting in the new first node and unlink it
  336. if(!end_found){
  337. base_t::unlink_after(base_t::get_previous_node(first, p));
  338. }
  339. //Now link p after the new last node
  340. base_t::link_after(new_last, p);
  341. return new_last;
  342. }
  343. //! <b>Effects</b>: Moves the node p n positions towards the beginning of the list.
  344. //!
  345. //! <b>Returns</b>: The previous node of p after the function if there has been any movement,
  346. //! Null if n leads equals to no movement.
  347. //!
  348. //! <b>Throws</b>: Nothing.
  349. //!
  350. //! <b>Complexity</b>: Linear to the number of elements plus the number moved positions.
  351. static node_ptr move_forward(node_ptr p, std::size_t n) BOOST_NOEXCEPT
  352. {
  353. //Null shift, nothing to do
  354. if(!n) return node_ptr();
  355. node_ptr first = node_traits::get_next(p);
  356. //count() == 1 or 2, nothing to do
  357. if(node_traits::get_next(first) == p) return node_ptr();
  358. //Iterate until p is found to know where the current last node is.
  359. //If the shift count is less than the size of the list, we can also obtain
  360. //the position of the new last node after the shift.
  361. node_ptr old_last(first), next_to_it, new_last(p);
  362. std::size_t distance = 1;
  363. while(p != (next_to_it = node_traits::get_next(old_last))){
  364. if(++distance > n)
  365. new_last = node_traits::get_next(new_last);
  366. old_last = next_to_it;
  367. }
  368. //If the shift was bigger or equal than the size, obtain the equivalent
  369. //forward shifts and find the new last node.
  370. if(distance <= n){
  371. //Now find the equivalent forward shifts.
  372. //Shortcut the shift with the modulo of the size of the list
  373. std::size_t new_before_last_pos = (distance - (n % distance))% distance;
  374. //If the shift is a multiple of the size there is nothing to do
  375. if(!new_before_last_pos) return node_ptr();
  376. for( new_last = p
  377. ; new_before_last_pos--
  378. ; new_last = node_traits::get_next(new_last)){
  379. //empty
  380. }
  381. }
  382. //Now unlink p and link it after the new last node
  383. base_t::unlink_after(old_last);
  384. base_t::link_after(new_last, p);
  385. return new_last;
  386. }
  387. //! <b>Requires</b>: other must be a list and p must be a node of a different list.
  388. //!
  389. //! <b>Effects</b>: Transfers all nodes from other after p in p's list.
  390. //!
  391. //! <b>Complexity</b>: Linear
  392. //!
  393. //! <b>Throws</b>: Nothing.
  394. static void transfer_after(node_ptr p, node_ptr other) BOOST_NOEXCEPT
  395. {
  396. node_ptr other_last((get_previous_node)(other));
  397. base_t::transfer_after(p, other, other_last);
  398. }
  399. //! <b>Requires</b>: "disposer" must be an object function
  400. //! taking a node_ptr parameter and shouldn't throw.
  401. //!
  402. //! <b>Effects</b>: Unlinks all nodes reachable from p (but not p) and calls
  403. //! <tt>void disposer::operator()(node_ptr)</tt> for every node of the list
  404. //! where p is linked.
  405. //!
  406. //! <b>Returns</b>: The number of disposed nodes
  407. //!
  408. //! <b>Complexity</b>: Linear to the number of element of the list.
  409. //!
  410. //! <b>Throws</b>: Nothing.
  411. template<class Disposer>
  412. inline static std::size_t detach_and_dispose(node_ptr p, Disposer disposer) BOOST_NOEXCEPT
  413. { return base_t::unlink_after_and_dispose(p, p, disposer); }
  414. };
  415. /// @cond
  416. template<class NodeTraits>
  417. struct get_algo<CircularSListAlgorithms, NodeTraits>
  418. {
  419. typedef circular_slist_algorithms<NodeTraits> type;
  420. };
  421. /// @endcond
  422. } //namespace intrusive
  423. } //namespace boost
  424. #include <boost/intrusive/detail/config_end.hpp>
  425. #endif //BOOST_INTRUSIVE_CIRCULAR_SLIST_ALGORITHMS_HPP