1 /////////////////////////////////////////////////////////////////////////////
3 // (C) Copyright Olaf Krzikalla 2004-2006.
4 // (C) Copyright Ion Gaztanaga 2006-2008
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)
10 // See http://www.boost.org/libs/intrusive for documentation.
12 /////////////////////////////////////////////////////////////////////////////
14 #ifndef BOOST_INTRUSIVE_CIRCULAR_SLIST_ALGORITHMS_HPP
15 #define BOOST_INTRUSIVE_CIRCULAR_SLIST_ALGORITHMS_HPP
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/assert.hpp>
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.
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:
36 //! <tt>node</tt>: The type of the node that forms the circular list
38 //! <tt>node_ptr</tt>: A pointer to a node
40 //! <tt>const_node_ptr</tt>: A pointer to a const node
42 //! <b>Static functions</b>:
44 //! <tt>static node_ptr get_next(const_node_ptr n);</tt>
46 //! <tt>static void set_next(node_ptr n, node_ptr next);</tt>
47 template<class NodeTraits
>
48 class circular_slist_algorithms
50 : public detail::common_slist_algorithms
<NodeTraits
>
54 typedef detail::common_slist_algorithms
<NodeTraits
> base_t
;
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
;
62 #if defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED)
64 //! <b>Effects</b>: Constructs an non-used list element, putting the next
66 //! <tt>NodeTraits::get_next(this_node) == 0
68 //! <b>Complexity</b>: Constant
70 //! <b>Throws</b>: Nothing.
71 static void init(node_ptr this_node
);
73 //! <b>Requires</b>: this_node must be in a circular list or be an empty circular list.
75 //! <b>Effects</b>: Returns true is "this_node" is the only node of a circular list:
76 //! or it's a not inserted node:
77 //! <tt>return !NodeTraits::get_next(this_node) || NodeTraits::get_next(this_node) == this_node</tt>
79 //! <b>Complexity</b>: Constant
81 //! <b>Throws</b>: Nothing.
82 static bool unique(const_node_ptr this_node
);
84 //! <b>Effects</b>: Returns true is "this_node" has the same state as
85 //! if it was inited using "init(node_ptr)"
87 //! <b>Complexity</b>: Constant
89 //! <b>Throws</b>: Nothing.
90 static bool inited(const_node_ptr this_node
);
92 //! <b>Requires</b>: prev_node must be in a circular list or be an empty circular list.
94 //! <b>Effects</b>: Unlinks the next node of prev_node from the circular list.
96 //! <b>Complexity</b>: Constant
98 //! <b>Throws</b>: Nothing.
99 static void unlink_after(node_ptr prev_node
);
101 //! <b>Requires</b>: prev_node and last_node must be in a circular list
102 //! or be an empty circular list.
104 //! <b>Effects</b>: Unlinks the range (prev_node, last_node) from the circular list.
106 //! <b>Complexity</b>: Constant
108 //! <b>Throws</b>: Nothing.
109 static void unlink_after(node_ptr prev_node
, node_ptr last_node
);
111 //! <b>Requires</b>: prev_node must be a node of a circular list.
113 //! <b>Effects</b>: Links this_node after prev_node in the circular list.
115 //! <b>Complexity</b>: Constant
117 //! <b>Throws</b>: Nothing.
118 static void link_after(node_ptr prev_node
, node_ptr this_node
);
120 //! <b>Requires</b>: b and e must be nodes of the same circular list or an empty range.
121 //! and p must be a node of a different circular list.
123 //! <b>Effects</b>: Removes the nodes from (b, e] range from their circular list and inserts
124 //! them after p in p's circular list.
126 //! <b>Complexity</b>: Constant
128 //! <b>Throws</b>: Nothing.
129 static void transfer_after(node_ptr p
, node_ptr b
, node_ptr e
);
131 #endif //#if defined(BOOST_INTRUSIVE_DOXYGEN_INVOKED)
133 //! <b>Effects</b>: Constructs an empty list, making this_node the only
134 //! node of the circular list:
135 //! <tt>NodeTraits::get_next(this_node) == this_node</tt>.
137 //! <b>Complexity</b>: Constant
139 //! <b>Throws</b>: Nothing.
140 static void init_header(node_ptr this_node
)
141 { NodeTraits::set_next(this_node
, this_node
); }
143 //! <b>Requires</b>: this_node and prev_init_node must be in the same circular list.
145 //! <b>Effects</b>: Returns the previous node of this_node in the circular list starting.
146 //! the search from prev_init_node. The first node checked for equality
147 //! is NodeTraits::get_next(prev_init_node).
149 //! <b>Complexity</b>: Linear to the number of elements between prev_init_node and this_node.
151 //! <b>Throws</b>: Nothing.
152 static node_ptr
get_previous_node(node_ptr prev_init_node
, node_ptr this_node
)
153 { return base_t::get_previous_node(prev_init_node
, this_node
); }
155 //! <b>Requires</b>: this_node must be in a circular list or be an empty circular list.
157 //! <b>Effects</b>: Returns the previous node of this_node in the circular list.
159 //! <b>Complexity</b>: Linear to the number of elements in the circular list.
161 //! <b>Throws</b>: Nothing.
162 static node_ptr
get_previous_node(node_ptr this_node
)
163 { return base_t::get_previous_node(this_node
, this_node
); }
165 //! <b>Requires</b>: this_node must be in a circular list or be an empty circular list.
167 //! <b>Effects</b>: Returns the previous node of the previous node of this_node in the circular list.
169 //! <b>Complexity</b>: Linear to the number of elements in the circular list.
171 //! <b>Throws</b>: Nothing.
172 static node_ptr
get_previous_previous_node(node_ptr this_node
)
173 { return get_previous_previous_node(this_node
, this_node
); }
175 //! <b>Requires</b>: this_node and prev_prev_init_node must be in the same circular list.
177 //! <b>Effects</b>: Returns the previous node of the previous node of this_node in the
178 //! circular list starting. the search from prev_init_node. The first node checked
179 //! for equality is NodeTraits::get_next((NodeTraits::get_next(prev_prev_init_node)).
181 //! <b>Complexity</b>: Linear to the number of elements in the circular list.
183 //! <b>Throws</b>: Nothing.
184 static node_ptr
get_previous_previous_node(node_ptr prev_prev_init_node
, node_ptr this_node
)
186 node_ptr p
= prev_prev_init_node
;
187 node_ptr p_next
= NodeTraits::get_next(p
);
188 node_ptr p_next_next
= NodeTraits::get_next(p_next
);
189 while (this_node
!= p_next_next
){
191 p_next
= p_next_next
;
192 p_next_next
= NodeTraits::get_next(p_next
);
197 //! <b>Requires</b>: this_node must be in a circular list or be an empty circular list.
199 //! <b>Effects</b>: Returns the number of nodes in a circular list. If the circular list
200 //! is empty, returns 1.
202 //! <b>Complexity</b>: Constant
204 //! <b>Throws</b>: Nothing.
205 static std::size_t count(const_node_ptr this_node
)
207 std::size_t result
= 0;
208 const_node_ptr p
= this_node
;
210 p
= NodeTraits::get_next(p
);
212 } while (p
!= this_node
);
216 //! <b>Requires</b>: this_node must be in a circular list, be an empty circular list or be inited.
218 //! <b>Effects</b>: Unlinks the node from the circular list.
220 //! <b>Complexity</b>: Linear to the number of elements in the circular list
222 //! <b>Throws</b>: Nothing.
223 static void unlink(node_ptr this_node
)
225 if(NodeTraits::get_next(this_node
))
226 base_t::unlink_after(get_previous_node(this_node
));
229 //! <b>Requires</b>: nxt_node must be a node of a circular list.
231 //! <b>Effects</b>: Links this_node before nxt_node in the circular list.
233 //! <b>Complexity</b>: Linear to the number of elements in the circular list.
235 //! <b>Throws</b>: Nothing.
236 static void link_before (node_ptr nxt_node
, node_ptr this_node
)
237 { base_t::link_after(get_previous_node(nxt_node
), this_node
); }
239 //! <b>Requires</b>: this_node and other_node must be nodes inserted
240 //! in circular lists or be empty circular lists.
242 //! <b>Effects</b>: Swaps the position of the nodes: this_node is inserted in
243 //! other_nodes position in the second circular list and the other_node is inserted
244 //! in this_node's position in the first circular list.
246 //! <b>Complexity</b>: Linear to number of elements of both lists
248 //! <b>Throws</b>: Nothing.
249 static void swap_nodes(node_ptr this_node
, node_ptr other_node
)
251 if (other_node
== this_node
)
253 bool this_inited
= base_t::inited(this_node
);
254 bool other_inited
= base_t::inited(other_node
);
256 base_t::init_header(this_node
);
259 base_t::init_header(other_node
);
262 bool empty1
= base_t::unique(this_node
);
263 bool empty2
= base_t::unique(other_node
);
264 node_ptr
prev_this (get_previous_node(this_node
));
265 node_ptr
prev_other(get_previous_node(other_node
));
267 node_ptr
this_next (NodeTraits::get_next(this_node
));
268 node_ptr
other_next(NodeTraits::get_next(other_node
));
269 NodeTraits::set_next(this_node
, other_next
);
270 NodeTraits::set_next(other_node
, this_next
);
271 NodeTraits::set_next(empty1
? other_node
: prev_this
, other_node
);
272 NodeTraits::set_next(empty2
? this_node
: prev_other
, this_node
);
275 base_t::init(other_node
);
278 base_t::init(this_node
);
282 //! <b>Effects</b>: Reverses the order of elements in the list.
284 //! <b>Throws</b>: Nothing.
286 //! <b>Complexity</b>: This function is linear to the contained elements.
287 static void reverse(node_ptr p
)
289 node_ptr i
= NodeTraits::get_next(p
), e(p
);
291 node_ptr
nxt(NodeTraits::get_next(i
));
294 base_t::transfer_after(e
, i
, nxt
);
298 //! <b>Effects</b>: Moves the node p n positions towards the end of the list.
300 //! <b>Returns</b>: The previous node of p after the function if there has been any movement,
301 //! Null if n leads to no movement.
303 //! <b>Throws</b>: Nothing.
305 //! <b>Complexity</b>: Linear to the number of elements plus the number moved positions.
306 static node_ptr
move_backwards(node_ptr p
, std::size_t n
)
308 //Null shift, nothing to do
309 if(!n
) return node_ptr(0);
310 node_ptr first
= NodeTraits::get_next(p
);
312 //count() == 1 or 2, nothing to do
313 if(NodeTraits::get_next(first
) == p
)
316 bool end_found
= false;
317 node_ptr
new_last(0);
319 //Now find the new last node according to the shift count.
320 //If we find p before finding the new last node
321 //unlink p, shortcut the search now that we know the size of the list
323 for(std::size_t i
= 1; i
<= n
; ++i
){
325 first
= NodeTraits::get_next(first
);
327 //Shortcut the shift with the modulo of the size of the list
332 //Unlink p and continue the new first node search
333 first
= NodeTraits::get_next(p
);
334 base_t::unlink_after(new_last
);
339 //If the p has not been found in the previous loop, find it
340 //starting in the new first node and unlink it
342 base_t::unlink_after(base_t::get_previous_node(first
, p
));
345 //Now link p after the new last node
346 base_t::link_after(new_last
, p
);
350 //! <b>Effects</b>: Moves the node p n positions towards the beginning of the list.
352 //! <b>Returns</b>: The previous node of p after the function if there has been any movement,
353 //! Null if n leads equals to no movement.
355 //! <b>Throws</b>: Nothing.
357 //! <b>Complexity</b>: Linear to the number of elements plus the number moved positions.
358 static node_ptr
move_forward(node_ptr p
, std::size_t n
)
360 //Null shift, nothing to do
361 if(!n
) return node_ptr(0);
362 node_ptr first
= node_traits::get_next(p
);
364 //count() == 1 or 2, nothing to do
365 if(node_traits::get_next(first
) == p
) return node_ptr(0);
367 //Iterate until p is found to know where the current last node is.
368 //If the shift count is less than the size of the list, we can also obtain
369 //the position of the new last node after the shift.
370 node_ptr
old_last(first
), next_to_it
, new_last(p
);
371 std::size_t distance
= 1;
372 while(p
!= (next_to_it
= node_traits::get_next(old_last
))){
374 new_last
= node_traits::get_next(new_last
);
375 old_last
= next_to_it
;
377 //If the shift was bigger or equal than the size, obtain the equivalent
378 //forward shifts and find the new last node.
380 //Now find the equivalent forward shifts.
381 //Shortcut the shift with the modulo of the size of the list
382 std::size_t new_before_last_pos
= (distance
- (n
% distance
))% distance
;
383 //If the shift is a multiple of the size there is nothing to do
384 if(!new_before_last_pos
) return node_ptr(0);
387 ; new_before_last_pos
--
388 ; new_last
= node_traits::get_next(new_last
)){
393 //Now unlink p and link it after the new last node
394 base_t::unlink_after(old_last
);
395 base_t::link_after(new_last
, p
);
400 } //namespace intrusive
403 #include <boost/intrusive/detail/config_end.hpp>
405 #endif //BOOST_INTRUSIVE_CIRCULAR_SLIST_ALGORITHMS_HPP