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