]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gdbsupport/intrusive_list.h
Automatic Copyright Year update after running gdb/copyright.py
[thirdparty/binutils-gdb.git] / gdbsupport / intrusive_list.h
CommitLineData
bf809310 1/* Intrusive double linked list for GDB, the GNU debugger.
4a94e368 2 Copyright (C) 2021-2022 Free Software Foundation, Inc.
bf809310
PA
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
8b6a69b2
SM
353 /* Move elements from LIST at the end of the current list. */
354 void splice (intrusive_list &&other)
355 {
356 if (other.empty ())
357 return;
358
359 if (this->empty ())
360 {
361 *this = std::move (other);
362 return;
363 }
364
365 /* [A ... B] + [C ... D] */
366 T *b_elem = m_back;
367 node_type *b_node = as_node (b_elem);
368 T *c_elem = other.m_front;
369 node_type *c_node = as_node (c_elem);
370 T *d_elem = other.m_back;
371
372 b_node->next = c_elem;
373 c_node->prev = b_elem;
374 m_back = d_elem;
375
376 other.m_front = nullptr;
377 other.m_back = nullptr;
378 }
379
bf809310
PA
380 void pop_front ()
381 {
382 gdb_assert (!this->empty ());
383 erase_element (*m_front);
384 }
385
386 void pop_back ()
387 {
388 gdb_assert (!this->empty ());
389 erase_element (*m_back);
390 }
391
392private:
393 /* Push ELEM in the list, knowing the list is empty. */
394 void push_empty (T &elem)
395 {
396 gdb_assert (this->empty ());
397
398 intrusive_list_node<T> *elem_node = as_node (&elem);
399
400 gdb_assert (elem_node->next == INTRUSIVE_LIST_UNLINKED_VALUE);
401 gdb_assert (elem_node->prev == INTRUSIVE_LIST_UNLINKED_VALUE);
402
403 m_front = &elem;
404 m_back = &elem;
405 elem_node->prev = nullptr;
406 elem_node->next = nullptr;
407 }
408
409 /* Push ELEM at the front of the list, knowing the list is not empty. */
410 void push_front_non_empty (T &elem)
411 {
412 gdb_assert (!this->empty ());
413
414 intrusive_list_node<T> *elem_node = as_node (&elem);
415 intrusive_list_node<T> *front_node = as_node (m_front);
416
417 gdb_assert (elem_node->next == INTRUSIVE_LIST_UNLINKED_VALUE);
418 gdb_assert (elem_node->prev == INTRUSIVE_LIST_UNLINKED_VALUE);
419
420 elem_node->next = m_front;
421 front_node->prev = &elem;
422 elem_node->prev = nullptr;
423 m_front = &elem;
424 }
425
426 /* Push ELEM at the back of the list, knowing the list is not empty. */
427 void push_back_non_empty (T &elem)
428 {
429 gdb_assert (!this->empty ());
430
431 intrusive_list_node<T> *elem_node = as_node (&elem);
432 intrusive_list_node<T> *back_node = as_node (m_back);
433
434 gdb_assert (elem_node->next == INTRUSIVE_LIST_UNLINKED_VALUE);
435 gdb_assert (elem_node->prev == INTRUSIVE_LIST_UNLINKED_VALUE);
436
437 elem_node->prev = m_back;
438 back_node->next = &elem;
439 elem_node->next = nullptr;
440 m_back = &elem;
441 }
442
443 void erase_element (T &elem)
444 {
445 intrusive_list_node<T> *elem_node = as_node (&elem);
446
447 gdb_assert (elem_node->prev != INTRUSIVE_LIST_UNLINKED_VALUE);
448 gdb_assert (elem_node->next != INTRUSIVE_LIST_UNLINKED_VALUE);
449
450 if (m_front == &elem)
451 {
452 gdb_assert (elem_node->prev == nullptr);
453 m_front = elem_node->next;
454 }
455 else
456 {
457 gdb_assert (elem_node->prev != nullptr);
458 intrusive_list_node<T> *prev_node = as_node (elem_node->prev);
459 prev_node->next = elem_node->next;
460 }
461
462 if (m_back == &elem)
463 {
464 gdb_assert (elem_node->next == nullptr);
465 m_back = elem_node->prev;
466 }
467 else
468 {
469 gdb_assert (elem_node->next != nullptr);
470 intrusive_list_node<T> *next_node = as_node (elem_node->next);
471 next_node->prev = elem_node->prev;
472 }
473
474 elem_node->next = INTRUSIVE_LIST_UNLINKED_VALUE;
475 elem_node->prev = INTRUSIVE_LIST_UNLINKED_VALUE;
476 }
477
478public:
479 /* Remove the element pointed by I from the list. The element
480 pointed by I is not destroyed. */
481 iterator erase (const_iterator i)
482 {
483 iterator ret = i;
484 ++ret;
485
486 erase_element (*i);
487
488 return ret;
489 }
490
491 /* Erase all the elements. The elements are not destroyed. */
492 void clear ()
493 {
494 while (!this->empty ())
495 pop_front ();
496 }
497
498 /* Erase all the elements. Disposer::operator()(pointer) is called
499 for each of the removed elements. */
500 template<typename Disposer>
501 void clear_and_dispose (Disposer disposer)
502 {
503 while (!this->empty ())
504 {
505 pointer p = &front ();
506 pop_front ();
507 disposer (p);
508 }
509 }
510
511 bool empty () const
512 {
513 return m_front == nullptr;
514 }
515
516 iterator begin () noexcept
517 {
518 return iterator (m_front);
519 }
520
521 const_iterator begin () const noexcept
522 {
523 return const_iterator (m_front);
524 }
525
526 const_iterator cbegin () const noexcept
527 {
528 return const_iterator (m_front);
529 }
530
531 iterator end () noexcept
532 {
533 return {};
534 }
535
536 const_iterator end () const noexcept
537 {
538 return {};
539 }
540
541 const_iterator cend () const noexcept
542 {
543 return {};
544 }
545
546 reverse_iterator rbegin () noexcept
547 {
548 return reverse_iterator (m_back);
549 }
550
551 const_reverse_iterator rbegin () const noexcept
552 {
553 return const_reverse_iterator (m_back);
554 }
555
556 const_reverse_iterator crbegin () const noexcept
557 {
558 return const_reverse_iterator (m_back);
559 }
560
561 reverse_iterator rend () noexcept
562 {
563 return {};
564 }
565
566 const_reverse_iterator rend () const noexcept
567 {
568 return {};
569 }
570
571 const_reverse_iterator crend () const noexcept
572 {
573 return {};
574 }
575
576private:
577 static node_type *as_node (T *elem)
578 {
579 return AsNode::as_node (elem);
580 }
581
582 T *m_front = nullptr;
583 T *m_back = nullptr;
584};
585
586#endif /* GDBSUPPORT_INTRUSIVE_LIST_H */