1 /* Intrusive double linked list for GDB, the GNU debugger.
2 Copyright (C) 2021-2024 Free Software Foundation, Inc.
4 This file is part of GDB.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #ifndef GDBSUPPORT_INTRUSIVE_LIST_H
20 #define GDBSUPPORT_INTRUSIVE_LIST_H
22 #define INTRUSIVE_LIST_UNLINKED_VALUE ((T *) -1)
24 /* A list node. The elements put in an intrusive_list either inherit
25 from this, or have a field of this type. */
27 class intrusive_list_node
30 bool is_linked () const noexcept
32 return next
!= INTRUSIVE_LIST_UNLINKED_VALUE
;
36 T
*next
= INTRUSIVE_LIST_UNLINKED_VALUE
;
37 T
*prev
= INTRUSIVE_LIST_UNLINKED_VALUE
;
39 template<typename T2
, typename AsNode
>
40 friend struct intrusive_list_iterator
;
42 template<typename T2
, typename AsNode
>
43 friend struct intrusive_list_reverse_iterator
;
45 template<typename T2
, typename AsNode
>
46 friend struct intrusive_list
;
49 /* Follows a couple types used by intrusive_list as template parameter to find
50 the intrusive_list_node for a given element. One for lists where the
51 elements inherit intrusive_list_node, and another for elements that keep the
52 node as member field. */
54 /* For element types that inherit from intrusive_list_node. */
57 struct intrusive_base_node
59 static intrusive_list_node
<T
> *as_node (T
*elem
) noexcept
63 /* For element types that keep the node as member field. */
65 template<typename T
, intrusive_list_node
<T
> T::*MemberNode
>
66 struct intrusive_member_node
68 static intrusive_list_node
<T
> *as_node (T
*elem
) noexcept
69 { return &(elem
->*MemberNode
); }
72 /* Common code for forward and reverse iterators. */
74 template<typename T
, typename AsNode
, typename SelfType
>
75 struct intrusive_list_base_iterator
77 using self_type
= SelfType
;
78 using iterator_category
= std::bidirectional_iterator_tag
;
81 using const_pointer
= const T
*;
82 using reference
= T
&;
83 using const_reference
= const T
&;
84 using difference_type
= ptrdiff_t;
85 using size_type
= size_t;
86 using node_type
= intrusive_list_node
<T
>;
88 /* Create an iterator pointing to ELEM. */
89 explicit intrusive_list_base_iterator (pointer elem
) noexcept
93 /* Create a past-the-end iterator. */
94 intrusive_list_base_iterator () noexcept
98 reference
operator* () const noexcept
101 pointer
operator-> () const noexcept
104 bool operator== (const self_type
&other
) const noexcept
105 { return m_elem
== other
.m_elem
; }
107 bool operator!= (const self_type
&other
) const noexcept
108 { return m_elem
!= other
.m_elem
; }
111 static node_type
*as_node (pointer elem
) noexcept
112 { return AsNode::as_node (elem
); }
114 /* A past-end-the iterator points to the list's head. */
118 /* Forward iterator for an intrusive_list. */
120 template<typename T
, typename AsNode
= intrusive_base_node
<T
>>
121 struct intrusive_list_iterator
122 : public intrusive_list_base_iterator
123 <T
, AsNode
, intrusive_list_iterator
<T
, AsNode
>>
125 using base
= intrusive_list_base_iterator
126 <T
, AsNode
, intrusive_list_iterator
<T
, AsNode
>>;
127 using self_type
= typename
base::self_type
;
128 using node_type
= typename
base::node_type
;
130 /* Inherit constructor and M_NODE visibility from base. */
134 self_type
&operator++ () noexcept
136 node_type
*node
= this->as_node (m_elem
);
141 self_type
operator++ (int) noexcept
143 self_type temp
= *this;
144 node_type
*node
= this->as_node (m_elem
);
149 self_type
&operator-- () noexcept
151 node_type
*node
= this->as_node (m_elem
);
156 self_type
operator-- (int) noexcept
158 self_type temp
= *this;
159 node_type
*node
= this->as_node (m_elem
);
165 /* Reverse iterator for an intrusive_list. */
167 template<typename T
, typename AsNode
= intrusive_base_node
<T
>>
168 struct intrusive_list_reverse_iterator
169 : public intrusive_list_base_iterator
170 <T
, AsNode
, intrusive_list_reverse_iterator
<T
, AsNode
>>
172 using base
= intrusive_list_base_iterator
173 <T
, AsNode
, intrusive_list_reverse_iterator
<T
, AsNode
>>;
174 using self_type
= typename
base::self_type
;
176 /* Inherit constructor and M_NODE visibility from base. */
179 using node_type
= typename
base::node_type
;
181 self_type
&operator++ () noexcept
183 node_type
*node
= this->as_node (m_elem
);
188 self_type
operator++ (int) noexcept
190 self_type temp
= *this;
191 node_type
*node
= this->as_node (m_elem
);
196 self_type
&operator-- () noexcept
198 node_type
*node
= this->as_node (m_elem
);
203 self_type
operator-- (int) noexcept
205 self_type temp
= *this;
206 node_type
*node
= this->as_node (m_elem
);
212 /* An intrusive double-linked list.
214 T is the type of the elements to link. The type T must either:
216 - inherit from intrusive_list_node<T>
217 - have an intrusive_list_node<T> member
219 AsNode is a type with an as_node static method used to get a node from an
220 element. If elements inherit from intrusive_list_node<T>, use the default
221 intrusive_base_node<T>. If elements have an intrusive_list_node<T> member,
224 intrusive_member_node<T, &T::member>
226 where `member` is the name of the member. */
228 template <typename T
, typename AsNode
= intrusive_base_node
<T
>>
232 using value_type
= T
;
234 using const_pointer
= const T
*;
235 using reference
= T
&;
236 using const_reference
= const T
&;
237 using difference_type
= ptrdiff_t;
238 using size_type
= size_t;
239 using iterator
= intrusive_list_iterator
<T
, AsNode
>;
240 using reverse_iterator
= intrusive_list_reverse_iterator
<T
, AsNode
>;
241 using const_iterator
= const intrusive_list_iterator
<T
, AsNode
>;
242 using const_reverse_iterator
243 = const intrusive_list_reverse_iterator
<T
, AsNode
>;
244 using node_type
= intrusive_list_node
<T
>;
246 intrusive_list () noexcept
= default;
253 intrusive_list (intrusive_list
&&other
) noexcept
254 : m_front (other
.m_front
),
255 m_back (other
.m_back
)
257 other
.m_front
= nullptr;
258 other
.m_back
= nullptr;
261 intrusive_list
&operator= (intrusive_list
&&other
) noexcept
263 m_front
= other
.m_front
;
264 m_back
= other
.m_back
;
265 other
.m_front
= nullptr;
266 other
.m_back
= nullptr;
271 void swap (intrusive_list
&other
) noexcept
273 std::swap (m_front
, other
.m_front
);
274 std::swap (m_back
, other
.m_back
);
277 iterator
iterator_to (reference value
) noexcept
279 return iterator (&value
);
282 const_iterator
iterator_to (const_reference value
) noexcept
284 return const_iterator (&value
);
287 reference
front () noexcept
289 gdb_assert (!this->empty ());
293 const_reference
front () const noexcept
295 gdb_assert (!this->empty ());
299 reference
back () noexcept
301 gdb_assert (!this->empty ());
305 const_reference
back () const noexcept
307 gdb_assert (!this->empty ());
311 void push_front (reference elem
) noexcept
313 intrusive_list_node
<T
> *elem_node
= as_node (&elem
);
315 gdb_assert (elem_node
->next
== INTRUSIVE_LIST_UNLINKED_VALUE
);
316 gdb_assert (elem_node
->prev
== INTRUSIVE_LIST_UNLINKED_VALUE
);
319 this->push_empty (elem
);
321 this->push_front_non_empty (elem
);
324 void push_back (reference elem
) noexcept
326 intrusive_list_node
<T
> *elem_node
= as_node (&elem
);
328 gdb_assert (elem_node
->next
== INTRUSIVE_LIST_UNLINKED_VALUE
);
329 gdb_assert (elem_node
->prev
== INTRUSIVE_LIST_UNLINKED_VALUE
);
332 this->push_empty (elem
);
334 this->push_back_non_empty (elem
);
337 /* Inserts ELEM before POS.
339 Returns an iterator to the inserted element. */
340 iterator
insert (const_iterator pos
, reference elem
) noexcept
343 return this->push_empty (elem
);
345 if (pos
== this->begin ())
346 return this->push_front_non_empty (elem
);
348 if (pos
== this->end ())
349 return this->push_back_non_empty (elem
);
351 intrusive_list_node
<T
> *elem_node
= as_node (&elem
);
352 pointer pos_elem
= &*pos
;
353 intrusive_list_node
<T
> *pos_node
= as_node (pos_elem
);
354 pointer prev_elem
= pos_node
->prev
;
355 intrusive_list_node
<T
> *prev_node
= as_node (prev_elem
);
357 gdb_assert (elem_node
->next
== INTRUSIVE_LIST_UNLINKED_VALUE
);
358 gdb_assert (elem_node
->prev
== INTRUSIVE_LIST_UNLINKED_VALUE
);
360 elem_node
->prev
= prev_elem
;
361 prev_node
->next
= &elem
;
362 elem_node
->next
= pos_elem
;
363 pos_node
->prev
= &elem
;
365 return this->iterator_to (elem
);
368 /* Move elements from LIST at the end of the current list. */
369 void splice (intrusive_list
&&other
) noexcept
376 *this = std::move (other
);
380 /* [A ... B] + [C ... D] */
381 pointer b_elem
= m_back
;
382 node_type
*b_node
= as_node (b_elem
);
383 pointer c_elem
= other
.m_front
;
384 node_type
*c_node
= as_node (c_elem
);
385 pointer d_elem
= other
.m_back
;
387 b_node
->next
= c_elem
;
388 c_node
->prev
= b_elem
;
391 other
.m_front
= nullptr;
392 other
.m_back
= nullptr;
395 void pop_front () noexcept
397 gdb_assert (!this->empty ());
398 unlink_element (*m_front
);
401 void pop_back () noexcept
403 gdb_assert (!this->empty ());
404 unlink_element (*m_back
);
408 /* Push ELEM in the list, knowing the list is empty. */
409 iterator
push_empty (reference elem
) noexcept
411 gdb_assert (this->empty ());
413 intrusive_list_node
<T
> *elem_node
= as_node (&elem
);
415 gdb_assert (elem_node
->next
== INTRUSIVE_LIST_UNLINKED_VALUE
);
416 gdb_assert (elem_node
->prev
== INTRUSIVE_LIST_UNLINKED_VALUE
);
420 elem_node
->prev
= nullptr;
421 elem_node
->next
= nullptr;
423 return this->iterator_to (elem
);
426 /* Push ELEM at the front of the list, knowing the list is not empty. */
427 iterator
push_front_non_empty (reference elem
) noexcept
429 gdb_assert (!this->empty ());
431 intrusive_list_node
<T
> *elem_node
= as_node (&elem
);
432 intrusive_list_node
<T
> *front_node
= as_node (m_front
);
434 gdb_assert (elem_node
->next
== INTRUSIVE_LIST_UNLINKED_VALUE
);
435 gdb_assert (elem_node
->prev
== INTRUSIVE_LIST_UNLINKED_VALUE
);
437 elem_node
->next
= m_front
;
438 front_node
->prev
= &elem
;
439 elem_node
->prev
= nullptr;
442 return this->iterator_to (elem
);
445 /* Push ELEM at the back of the list, knowing the list is not empty. */
446 iterator
push_back_non_empty (reference elem
) noexcept
448 gdb_assert (!this->empty ());
450 intrusive_list_node
<T
> *elem_node
= as_node (&elem
);
451 intrusive_list_node
<T
> *back_node
= as_node (m_back
);
453 gdb_assert (elem_node
->next
== INTRUSIVE_LIST_UNLINKED_VALUE
);
454 gdb_assert (elem_node
->prev
== INTRUSIVE_LIST_UNLINKED_VALUE
);
456 elem_node
->prev
= m_back
;
457 back_node
->next
= &elem
;
458 elem_node
->next
= nullptr;
461 return this->iterator_to (elem
);
465 void unlink_element (reference elem
) noexcept
467 intrusive_list_node
<T
> *elem_node
= as_node (&elem
);
469 gdb_assert (elem_node
->prev
!= INTRUSIVE_LIST_UNLINKED_VALUE
);
470 gdb_assert (elem_node
->next
!= INTRUSIVE_LIST_UNLINKED_VALUE
);
472 if (m_front
== &elem
)
474 gdb_assert (elem_node
->prev
== nullptr);
475 m_front
= elem_node
->next
;
479 gdb_assert (elem_node
->prev
!= nullptr);
480 intrusive_list_node
<T
> *prev_node
= as_node (elem_node
->prev
);
481 prev_node
->next
= elem_node
->next
;
486 gdb_assert (elem_node
->next
== nullptr);
487 m_back
= elem_node
->prev
;
491 gdb_assert (elem_node
->next
!= nullptr);
492 intrusive_list_node
<T
> *next_node
= as_node (elem_node
->next
);
493 next_node
->prev
= elem_node
->prev
;
496 elem_node
->next
= INTRUSIVE_LIST_UNLINKED_VALUE
;
497 elem_node
->prev
= INTRUSIVE_LIST_UNLINKED_VALUE
;
501 /* Remove the element pointed by I from the list. The element
502 pointed by I is not destroyed. */
503 iterator
erase (const_iterator i
) noexcept
513 /* Erase all the elements. The elements are not destroyed. */
514 void clear () noexcept
516 while (!this->empty ())
520 /* Erase all the elements. Disposer::operator()(pointer) is called
521 for each of the removed elements. */
522 template<typename Disposer
>
523 void clear_and_dispose (Disposer disposer
) noexcept
525 while (!this->empty ())
527 pointer p
= &front ();
533 bool empty () const noexcept
535 return m_front
== nullptr;
538 iterator
begin () noexcept
540 return iterator (m_front
);
543 const_iterator
begin () const noexcept
545 return const_iterator (m_front
);
548 const_iterator
cbegin () const noexcept
550 return const_iterator (m_front
);
553 iterator
end () noexcept
558 const_iterator
end () const noexcept
563 const_iterator
cend () const noexcept
568 reverse_iterator
rbegin () noexcept
570 return reverse_iterator (m_back
);
573 const_reverse_iterator
rbegin () const noexcept
575 return const_reverse_iterator (m_back
);
578 const_reverse_iterator
crbegin () const noexcept
580 return const_reverse_iterator (m_back
);
583 reverse_iterator
rend () noexcept
588 const_reverse_iterator
rend () const noexcept
593 const_reverse_iterator
crend () const noexcept
599 static node_type
*as_node (pointer elem
) noexcept
601 return AsNode::as_node (elem
);
604 pointer m_front
= nullptr;
605 pointer m_back
= nullptr;
608 #endif /* GDBSUPPORT_INTRUSIVE_LIST_H */