]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gdbsupport/intrusive_list.h
gdb: introduce intrusive_list, make thread_info use it
[thirdparty/binutils-gdb.git] / gdbsupport / intrusive_list.h
CommitLineData
bf809310
PA
1/* Intrusive double linked list for GDB, the GNU debugger.
2 Copyright (C) 2021 Free Software Foundation, Inc.
3
4 This file is part of GDB.
5
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.
10
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.
15
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/>. */
18
19#ifndef GDBSUPPORT_INTRUSIVE_LIST_H
20#define GDBSUPPORT_INTRUSIVE_LIST_H
21
22#define INTRUSIVE_LIST_UNLINKED_VALUE ((T *) -1)
23
24/* A list node. The elements put in an intrusive_list either inherit
25 from this, or have a field of this type. */
26template<typename T>
27struct intrusive_list_node
28{
29 bool is_linked () const
30 {
31 return next != INTRUSIVE_LIST_UNLINKED_VALUE;
32 }
33
34 T *next = INTRUSIVE_LIST_UNLINKED_VALUE;
35 T *prev = INTRUSIVE_LIST_UNLINKED_VALUE;
36};
37
38/* Follows a couple types used by intrusive_list as template parameter to find
39 the intrusive_list_node for a given element. One for lists where the
40 elements inherit intrusive_list_node, and another for elements that keep the
41 node as member field. */
42
43/* For element types that inherit from intrusive_list_node. */
44
45template<typename T>
46struct intrusive_base_node
47{
48 static intrusive_list_node<T> *as_node (T *elem)
49 { return elem; }
50};
51
52/* For element types that keep the node as member field. */
53
54template<typename T, intrusive_list_node<T> T::*MemberNode>
55struct intrusive_member_node
56{
57 static intrusive_list_node<T> *as_node (T *elem)
58 { return &(elem->*MemberNode); }
59};
60
61/* Common code for forward and reverse iterators. */
62
63template<typename T, typename AsNode, typename SelfType>
64struct intrusive_list_base_iterator
65{
66 using self_type = SelfType;
67 using iterator_category = std::bidirectional_iterator_tag;
68 using value_type = T;
69 using pointer = T *;
70 using const_pointer = const T *;
71 using reference = T &;
72 using const_reference = const T &;
73 using difference_type = ptrdiff_t;
74 using size_type = size_t;
75 using node_type = intrusive_list_node<T>;
76
77 /* Create an iterator pointing to ELEM. */
78 explicit intrusive_list_base_iterator (T *elem)
79 : m_elem (elem)
80 {}
81
82 /* Create a past-the-end iterator. */
83 intrusive_list_base_iterator ()
84 : m_elem (nullptr)
85 {}
86
87 reference operator* () const
88 { return *m_elem; }
89
90 pointer operator-> () const
91 { return m_elem; }
92
93 bool operator== (const self_type &other) const
94 { return m_elem == other.m_elem; }
95
96 bool operator!= (const self_type &other) const
97 { return m_elem != other.m_elem; }
98
99protected:
100 static node_type *as_node (T *elem)
101 { return AsNode::as_node (elem); }
102
103 /* A past-end-the iterator points to the list's head. */
104 pointer m_elem;
105};
106
107/* Forward iterator for an intrusive_list. */
108
109template<typename T, typename AsNode = intrusive_base_node<T>>
110struct intrusive_list_iterator
111 : public intrusive_list_base_iterator
112 <T, AsNode, intrusive_list_iterator<T, AsNode>>
113{
114 using base = intrusive_list_base_iterator
115 <T, AsNode, intrusive_list_iterator<T, AsNode>>;
116 using self_type = typename base::self_type;
117 using node_type = typename base::node_type;
118
119 /* Inherit constructor and M_NODE visibility from base. */
120 using base::base;
121 using base::m_elem;
122
123 self_type &operator++ ()
124 {
125 node_type *node = this->as_node (m_elem);
126 m_elem = node->next;
127 return *this;
128 }
129
130 self_type operator++ (int)
131 {
132 self_type temp = *this;
133 node_type *node = this->as_node (m_elem);
134 m_elem = node->next;
135 return temp;
136 }
137
138 self_type &operator-- ()
139 {
140 node_type *node = this->as_node (m_elem);
141 m_elem = node->prev;
142 return *this;
143 }
144
145 self_type operator-- (int)
146 {
147 self_type temp = *this;
148 node_type *node = this->as_node (m_elem);
149 m_elem = node->prev;
150 return temp;
151 }
152};
153
154/* Reverse iterator for an intrusive_list. */
155
156template<typename T, typename AsNode = intrusive_base_node<T>>
157struct intrusive_list_reverse_iterator
158 : public intrusive_list_base_iterator
159 <T, AsNode, intrusive_list_reverse_iterator<T, AsNode>>
160{
161 using base = intrusive_list_base_iterator
162 <T, AsNode, intrusive_list_reverse_iterator<T, AsNode>>;
163 using self_type = typename base::self_type;
164
165 /* Inherit constructor and M_NODE visibility from base. */
166 using base::base;
167 using base::m_elem;
168 using node_type = typename base::node_type;
169
170 self_type &operator++ ()
171 {
172 node_type *node = this->as_node (m_elem);
173 m_elem = node->prev;
174 return *this;
175 }
176
177 self_type operator++ (int)
178 {
179 self_type temp = *this;
180 node_type *node = this->as_node (m_elem);
181 m_elem = node->prev;
182 return temp;
183 }
184
185 self_type &operator-- ()
186 {
187 node_type *node = this->as_node (m_elem);
188 m_elem = node->next;
189 return *this;
190 }
191
192 self_type operator-- (int)
193 {
194 self_type temp = *this;
195 node_type *node = this->as_node (m_elem);
196 m_elem = node->next;
197 return temp;
198 }
199};
200
201/* An intrusive double-linked list.
202
203 T is the type of the elements to link. The type T must either:
204
205 - inherit from intrusive_list_node<T>
206 - have an intrusive_list_node<T> member
207
208 AsNode is a type with an as_node static method used to get a node from an
209 element. If elements inherit from intrusive_list_node<T>, use the default
210 intrusive_base_node<T>. If elements have an intrusive_list_node<T> member,
211 use:
212
213 intrusive_member_node<T, &T::member>
214
215 where `member` is the name of the member. */
216
217template <typename T, typename AsNode = intrusive_base_node<T>>
218class intrusive_list
219{
220public:
221 using value_type = T;
222 using pointer = T *;
223 using const_pointer = const T *;
224 using reference = T &;
225 using const_reference = const T &;
226 using difference_type = ptrdiff_t;
227 using size_type = size_t;
228 using iterator = intrusive_list_iterator<T, AsNode>;
229 using reverse_iterator = intrusive_list_reverse_iterator<T, AsNode>;
230 using const_iterator = const intrusive_list_iterator<T, AsNode>;
231 using const_reverse_iterator
232 = const intrusive_list_reverse_iterator<T, AsNode>;
233 using node_type = intrusive_list_node<T>;
234
235 intrusive_list () = default;
236
237 ~intrusive_list ()
238 {
239 clear ();
240 }
241
242 intrusive_list (intrusive_list &&other)
243 : m_front (other.m_front),
244 m_back (other.m_back)
245 {
246 other.m_front = nullptr;
247 other.m_back = nullptr;
248 }
249
250 intrusive_list &operator= (intrusive_list &&other)
251 {
252 m_front = other.m_front;
253 m_back = other.m_back;
254 other.m_front = nullptr;
255 other.m_back = nullptr;
256
257 return *this;
258 }
259
260 void swap (intrusive_list &other)
261 {
262 std::swap (m_front, other.m_front);
263 std::swap (m_back, other.m_back);
264 }
265
266 iterator iterator_to (reference value)
267 {
268 return iterator (&value);
269 }
270
271 const_iterator iterator_to (const_reference value)
272 {
273 return const_iterator (&value);
274 }
275
276 reference front ()
277 {
278 gdb_assert (!this->empty ());
279 return *m_front;
280 }
281
282 const_reference front () const
283 {
284 gdb_assert (!this->empty ());
285 return *m_front;
286 }
287
288 reference back ()
289 {
290 gdb_assert (!this->empty ());
291 return *m_back;
292 }
293
294 const_reference back () const
295 {
296 gdb_assert (!this->empty ());
297 return *m_back;
298 }
299
300 void push_front (reference elem)
301 {
302 intrusive_list_node<T> *elem_node = as_node (&elem);
303
304 gdb_assert (elem_node->next == INTRUSIVE_LIST_UNLINKED_VALUE);
305 gdb_assert (elem_node->prev == INTRUSIVE_LIST_UNLINKED_VALUE);
306
307 if (this->empty ())
308 this->push_empty (elem);
309 else
310 this->push_front_non_empty (elem);
311 }
312
313 void push_back (reference elem)
314 {
315 intrusive_list_node<T> *elem_node = as_node (&elem);
316
317 gdb_assert (elem_node->next == INTRUSIVE_LIST_UNLINKED_VALUE);
318 gdb_assert (elem_node->prev == INTRUSIVE_LIST_UNLINKED_VALUE);
319
320 if (this->empty ())
321 this->push_empty (elem);
322 else
323 this->push_back_non_empty (elem);
324 }
325
326 /* Inserts ELEM before POS. */
327 void insert (const_iterator pos, reference elem)
328 {
329 if (this->empty ())
330 return this->push_empty (elem);
331
332 if (pos == this->begin ())
333 return this->push_front_non_empty (elem);
334
335 if (pos == this->end ())
336 return this->push_back_non_empty (elem);
337
338 intrusive_list_node<T> *elem_node = as_node (&elem);
339 T *pos_elem = &*pos;
340 intrusive_list_node<T> *pos_node = as_node (pos_elem);
341 T *prev_elem = pos_node->prev;
342 intrusive_list_node<T> *prev_node = as_node (prev_elem);
343
344 gdb_assert (elem_node->next == INTRUSIVE_LIST_UNLINKED_VALUE);
345 gdb_assert (elem_node->prev == INTRUSIVE_LIST_UNLINKED_VALUE);
346
347 elem_node->prev = prev_elem;
348 prev_node->next = &elem;
349 elem_node->next = pos_elem;
350 pos_node->prev = &elem;
351 }
352
353 void pop_front ()
354 {
355 gdb_assert (!this->empty ());
356 erase_element (*m_front);
357 }
358
359 void pop_back ()
360 {
361 gdb_assert (!this->empty ());
362 erase_element (*m_back);
363 }
364
365private:
366 /* Push ELEM in the list, knowing the list is empty. */
367 void push_empty (T &elem)
368 {
369 gdb_assert (this->empty ());
370
371 intrusive_list_node<T> *elem_node = as_node (&elem);
372
373 gdb_assert (elem_node->next == INTRUSIVE_LIST_UNLINKED_VALUE);
374 gdb_assert (elem_node->prev == INTRUSIVE_LIST_UNLINKED_VALUE);
375
376 m_front = &elem;
377 m_back = &elem;
378 elem_node->prev = nullptr;
379 elem_node->next = nullptr;
380 }
381
382 /* Push ELEM at the front of the list, knowing the list is not empty. */
383 void push_front_non_empty (T &elem)
384 {
385 gdb_assert (!this->empty ());
386
387 intrusive_list_node<T> *elem_node = as_node (&elem);
388 intrusive_list_node<T> *front_node = as_node (m_front);
389
390 gdb_assert (elem_node->next == INTRUSIVE_LIST_UNLINKED_VALUE);
391 gdb_assert (elem_node->prev == INTRUSIVE_LIST_UNLINKED_VALUE);
392
393 elem_node->next = m_front;
394 front_node->prev = &elem;
395 elem_node->prev = nullptr;
396 m_front = &elem;
397 }
398
399 /* Push ELEM at the back of the list, knowing the list is not empty. */
400 void push_back_non_empty (T &elem)
401 {
402 gdb_assert (!this->empty ());
403
404 intrusive_list_node<T> *elem_node = as_node (&elem);
405 intrusive_list_node<T> *back_node = as_node (m_back);
406
407 gdb_assert (elem_node->next == INTRUSIVE_LIST_UNLINKED_VALUE);
408 gdb_assert (elem_node->prev == INTRUSIVE_LIST_UNLINKED_VALUE);
409
410 elem_node->prev = m_back;
411 back_node->next = &elem;
412 elem_node->next = nullptr;
413 m_back = &elem;
414 }
415
416 void erase_element (T &elem)
417 {
418 intrusive_list_node<T> *elem_node = as_node (&elem);
419
420 gdb_assert (elem_node->prev != INTRUSIVE_LIST_UNLINKED_VALUE);
421 gdb_assert (elem_node->next != INTRUSIVE_LIST_UNLINKED_VALUE);
422
423 if (m_front == &elem)
424 {
425 gdb_assert (elem_node->prev == nullptr);
426 m_front = elem_node->next;
427 }
428 else
429 {
430 gdb_assert (elem_node->prev != nullptr);
431 intrusive_list_node<T> *prev_node = as_node (elem_node->prev);
432 prev_node->next = elem_node->next;
433 }
434
435 if (m_back == &elem)
436 {
437 gdb_assert (elem_node->next == nullptr);
438 m_back = elem_node->prev;
439 }
440 else
441 {
442 gdb_assert (elem_node->next != nullptr);
443 intrusive_list_node<T> *next_node = as_node (elem_node->next);
444 next_node->prev = elem_node->prev;
445 }
446
447 elem_node->next = INTRUSIVE_LIST_UNLINKED_VALUE;
448 elem_node->prev = INTRUSIVE_LIST_UNLINKED_VALUE;
449 }
450
451public:
452 /* Remove the element pointed by I from the list. The element
453 pointed by I is not destroyed. */
454 iterator erase (const_iterator i)
455 {
456 iterator ret = i;
457 ++ret;
458
459 erase_element (*i);
460
461 return ret;
462 }
463
464 /* Erase all the elements. The elements are not destroyed. */
465 void clear ()
466 {
467 while (!this->empty ())
468 pop_front ();
469 }
470
471 /* Erase all the elements. Disposer::operator()(pointer) is called
472 for each of the removed elements. */
473 template<typename Disposer>
474 void clear_and_dispose (Disposer disposer)
475 {
476 while (!this->empty ())
477 {
478 pointer p = &front ();
479 pop_front ();
480 disposer (p);
481 }
482 }
483
484 bool empty () const
485 {
486 return m_front == nullptr;
487 }
488
489 iterator begin () noexcept
490 {
491 return iterator (m_front);
492 }
493
494 const_iterator begin () const noexcept
495 {
496 return const_iterator (m_front);
497 }
498
499 const_iterator cbegin () const noexcept
500 {
501 return const_iterator (m_front);
502 }
503
504 iterator end () noexcept
505 {
506 return {};
507 }
508
509 const_iterator end () const noexcept
510 {
511 return {};
512 }
513
514 const_iterator cend () const noexcept
515 {
516 return {};
517 }
518
519 reverse_iterator rbegin () noexcept
520 {
521 return reverse_iterator (m_back);
522 }
523
524 const_reverse_iterator rbegin () const noexcept
525 {
526 return const_reverse_iterator (m_back);
527 }
528
529 const_reverse_iterator crbegin () const noexcept
530 {
531 return const_reverse_iterator (m_back);
532 }
533
534 reverse_iterator rend () noexcept
535 {
536 return {};
537 }
538
539 const_reverse_iterator rend () const noexcept
540 {
541 return {};
542 }
543
544 const_reverse_iterator crend () const noexcept
545 {
546 return {};
547 }
548
549private:
550 static node_type *as_node (T *elem)
551 {
552 return AsNode::as_node (elem);
553 }
554
555 T *m_front = nullptr;
556 T *m_back = nullptr;
557};
558
559#endif /* GDBSUPPORT_INTRUSIVE_LIST_H */