Jlm
Loading...
Searching...
No Matches
intrusive-list.hpp
Go to the documentation of this file.
1/*
2 * Copyright 2014 2015 Helge Bahmann <hcb@chaoticmind.net>
3 * Copyright 2014 Nico Reißmann <nico.reissmann@gmail.com>
4 * See COPYING for terms of redistribution.
5 */
6
7#ifndef JLM_UTIL_INTRUSIVE_LIST_HPP
8#define JLM_UTIL_INTRUSIVE_LIST_HPP
9
10#include <iterator>
11#include <memory>
12
13/*
14 * Implementation of intrusive list data structure
15 *
16 * An intrusive list is a data structure linking multiple objects into a
17 * list. The difference to std::list is that the linkage pointers are part
18 * of the objects linked into the data structure itself. As a result, the
19 * intrusive list does not manage memory for the objects it contains, but
20 * links objects allocated elsewhere. Any object can be member of an arbitrary
21 * number of such intrusive list collections.
22 *
23 * Usage:
24 *
25 * For a class declared as the following:
26 *
27 * class X {
28 * private:
29 * int num;
30 * jlm::util::IntrusiveListAnchor<X> num_list_anchor;
31 * public:
32 * typedef jlm::util::IntrusiveListAccessor<
33 * X, // element type
34 * &X::num_list_anchor // anchor member
35 * > num_list_accessor;
36 * };
37 *
38 * an intrusive list data structure can be declared in the following way:
39 *
40 * typedef jlm::util::IntrusiveList<
41 * X,
42 * X::num_list_accessor
43 * > num_list;
44 *
45 * It is possible to implement a custom accessor instead of using the
46 * template-generated one. In this case the get_prev, get_next, set_prev and
47 * set_next members must be implemented appropriately.
48 *
49 * An object h of num_list class then supports STL-style operations
50 * - num_list::Iterator, num_list::ConstIterator for iteration
51 * - h.begin(), h.end() and const-qualified variants
52 * - h.insert(element) h.push_front(element), h.push_back(element) links an
53 * object into the data structure
54 * - h.erase(element) or h.erase(iterator) unlinks an object from the data
55 * structure
56 * - h.size() and h.empty() testing
57 *
58 * The implementation requires the following guarantees:
59 * - the accessor to key and anchor are noexcept
60 *
61 * The implementation provides the following guarantees
62 * - insert is O(1) amortized
63 * - erase, empty, are O(1) and noexcept
64 * - end is O(1)
65 * - begin is O(n)
66 * - inserting a new object does not invalidate iterators or change order
67 * of previously inserted objects
68 * - erasing an object does not invalidate iterators except those pointing
69 * to the object removed and does not change order of previously inserted
70 * objects
71 *
72 * An additional template OwnerIntrusiveList implements the same
73 * interface, but in addition assumes "ownership" of the objects it contains.
74 * This means that upon destruction of the container, the elements will
75 * be deleted as well. This variant differs in the following ways:
76 *
77 * - insert, push_back, push_front expect to be given a std::unique_ptr
78 * to the object to be inserted
79 * - a new method "unlink" removes an object from the list and returns
80 * a std::unique_ptr to the element
81 * - "erase" will actually cause an element to be deleted; likewise,
82 * all elements will be deleted on destruction of the OwnerIntrusiveList
83 */
84
85namespace jlm::util
86{
87
88template<typename ElementType, typename Accessor>
90{
91public:
92 static_assert(noexcept(Accessor().get_prev(nullptr)), "require noexcept get_prev");
93 static_assert(noexcept(Accessor().get_next(nullptr)), "require noexcept get_next");
94 static_assert(noexcept(Accessor().set_prev(nullptr, nullptr)), "require noexcept set_prev");
95 static_assert(noexcept(Accessor().set_next(nullptr, nullptr)), "require noexcept set_next");
96 class ConstIterator;
97
99 {
100 public:
101 typedef ElementType value_type;
102 typedef ElementType * pointer;
103 typedef ElementType & reference;
104 typedef std::bidirectional_iterator_tag iterator_category;
105 typedef size_t size_type;
106 typedef ssize_t difference_type;
107
108 constexpr Iterator() noexcept
109 : list_(nullptr),
110 element_(nullptr)
111 {}
112
113 constexpr Iterator(const IntrusiveList * list, ElementType * object)
114 : list_(list),
115 element_(object)
116 {}
117
118 const Iterator &
119 operator++() noexcept
120 {
121 element_ = list_->accessor_.get_next(element_);
122 return *this;
123 }
124
126 operator++(int) noexcept
127 {
128 Iterator i = *this;
129 ++*this;
130 return i;
131 }
132
133 const Iterator &
134 operator--() noexcept
135 {
136 if (element_)
137 {
138 element_ = list_->accessor_.get_prev(element_);
139 }
140 else
141 {
143 }
144 return *this;
145 }
146
148 operator--(int) noexcept
149 {
150 Iterator i = *this;
151 --*this;
152 return i;
153 }
154
155 inline ElementType &
156 operator*() const noexcept
157 {
158 return *element_;
159 }
160
161 inline ElementType *
162 operator->() const noexcept
163 {
164 return element_;
165 }
166
167 inline bool
168 operator==(const Iterator & other) const noexcept
169 {
170 return element_ == other.element_;
171 }
172
173 inline bool
174 operator!=(const Iterator & other) const noexcept
175 {
176 return element_ != other.element_;
177 }
178
179 inline ElementType *
180 ptr() const noexcept
181 {
182 return element_;
183 }
184
185 private:
187 ElementType * element_;
188 friend class ConstIterator;
189 };
190
192 {
193 public:
194 typedef const ElementType value_type;
195 typedef const ElementType * pointer;
196 typedef const ElementType & reference;
197 typedef std::bidirectional_iterator_tag iterator_category;
198 typedef size_t size_type;
199 typedef ssize_t difference_type;
200
201 constexpr ConstIterator(const ConstIterator & other) noexcept = default;
202
203 explicit constexpr ConstIterator(const Iterator & other) noexcept
204 : list_(other.list_),
205 element_(other.element_)
206 {}
207
208 constexpr ConstIterator() noexcept
209 : list_(nullptr),
210 element_(nullptr)
211 {}
212
213 constexpr ConstIterator(const IntrusiveList * list, const ElementType * object)
214 : list_(list),
215 element_(object)
216 {}
217
218 const ConstIterator &
219 operator++() noexcept
220 {
221 element_ = list_->accessor_.get_next(element_);
222 return *this;
223 }
224
226 operator++(int) noexcept
227 {
228 Iterator i = *this;
229 ++*this;
230 return i;
231 }
232
233 const ConstIterator &
234 operator--() noexcept
235 {
236 if (element_)
237 {
238 element_ = list_->accessor_.get_prev(element_);
239 }
240 else
241 {
243 }
244 return *this;
245 }
246
248 operator--(int) noexcept
249 {
250 Iterator i = *this;
251 --*this;
252 return i;
253 }
254
255 inline const ElementType &
256 operator*() const noexcept
257 {
258 return *element_;
259 }
260
261 inline const ElementType *
262 operator->() const noexcept
263 {
264 return element_;
265 }
266
267 inline bool
268 operator==(const ConstIterator & other) const noexcept
269 {
270 return element_ == other.element_;
271 }
272
273 inline bool
274 operator!=(const ConstIterator & other) const noexcept
275 {
276 return element_ != other.element_;
277 }
278
279 inline const ElementType *
280 ptr() const noexcept
281 {
282 return element_;
283 }
284
285 private:
287 const ElementType * element_;
288 };
289
290 typedef ElementType value_type;
291 typedef size_t size_type;
292
293 inline constexpr IntrusiveList() noexcept
294 : first_(nullptr),
295 last_(nullptr)
296 {}
297
298 IntrusiveList(const IntrusiveList & other) = delete;
299
300 void
301 operator=(const IntrusiveList & other) = delete;
302
303 IntrusiveList(IntrusiveList && other) noexcept
304 : IntrusiveList()
305 {
306 swap(other);
307 }
308
309 void
310 clear() noexcept
311 {
312 first_ = nullptr;
313 last_ = nullptr;
314 }
315
316 void
317 swap(IntrusiveList & other) noexcept
318 {
319 std::swap(first_, other.first_);
320 std::swap(last_, other.last_);
321 }
322
323 inline void
324 push_back(ElementType * element) noexcept
325 {
326 accessor_.set_prev(element, last_);
327 accessor_.set_next(element, nullptr);
328 if (last_)
329 {
330 accessor_.set_next(last_, element);
331 }
332 else
333 {
334 first_ = element;
335 }
336 last_ = element;
337 }
338
339 inline void
340 push_front(ElementType * element) noexcept
341 {
342 accessor_.set_prev(element, nullptr);
343 accessor_.set_next(element, first_);
344 if (first_)
345 {
346 accessor_.set_prev(first_, element);
347 }
348 else
349 {
350 last_ = element;
351 }
352 first_ = element;
353 }
354
355 Iterator
356 insert(Iterator i, ElementType * element) noexcept
357 {
358 ElementType * next = i.ptr();
359 ElementType * prev = next ? accessor_.get_prev(next) : last_;
360 accessor_.set_prev(element, prev);
361 accessor_.set_next(element, next);
362 if (prev)
363 {
364 accessor_.set_next(prev, element);
365 }
366 else
367 {
368 first_ = element;
369 }
370 if (next)
371 {
372 accessor_.set_prev(next, element);
373 }
374 else
375 {
376 last_ = element;
377 }
378 return Iterator(this, element);
379 }
380
381 inline void
382 erase(ElementType * element) noexcept
383 {
384 ElementType * prev = accessor_.get_prev(element);
385 ElementType * next = accessor_.get_next(element);
386 if (prev)
387 {
388 accessor_.set_next(prev, next);
389 }
390 else
391 {
392 first_ = next;
393 }
394 if (next)
395 {
396 accessor_.set_prev(next, prev);
397 }
398 else
399 {
400 last_ = prev;
401 }
402 }
403
404 Iterator
405 erase(Iterator i) noexcept
406 {
407 auto element = i.ptr();
408 ++i;
409 erase(element);
410 return i;
411 }
412
413 inline void
415 {
416 while (begin != end)
417 {
418 ElementType * element = begin.ptr();
419 ++begin;
420 erase(element);
421 }
422 }
423
424 inline void
425 splice(Iterator position, IntrusiveList & other) noexcept
426 {
427 splice(position, other, other.begin(), other.end());
428 }
429
430 inline void
431 splice(Iterator position, IntrusiveList & other, Iterator i) noexcept
432 {
433 Iterator j = i;
434 ++j;
435 splice(position, other, i, j);
436 }
437
438 inline void
439 splice(Iterator position, IntrusiveList & other, Iterator begin, Iterator end) noexcept
440 {
441 if (begin == end)
442 {
443 return;
444 }
445
446 ElementType * first = begin.ptr();
447 ElementType * before = accessor_.get_prev(first);
448 ElementType * after = end.ptr();
449 ElementType * last = after ? accessor_.get_prev(after) : other.last_;
450
451 /* unlink from source */
452 if (before)
453 {
454 accessor_.set_next(before, after);
455 }
456 else
457 {
458 other.first_ = after;
459 }
460 if (after)
461 {
462 accessor_.set_prev(after, before);
463 }
464 else
465 {
466 other.last_ = before;
467 }
468
469 /* link to destination */
470 ElementType * dst_next = position.ptr();
471 ElementType * dst_prev = dst_next ? accessor_.get_prev(dst_next) : last_;
472 accessor_.set_prev(first, dst_prev);
473 accessor_.set_next(last, dst_next);
474 if (dst_prev)
475 {
476 accessor_.set_next(dst_prev, first);
477 }
478 else
479 {
480 first_ = first;
481 }
482 if (dst_next)
483 {
484 accessor_.set_prev(dst_next, last);
485 }
486 else
487 {
488 last_ = last;
489 }
490 }
491
492 inline size_type
493 size() const noexcept
494 {
495 size_type count = 0;
496 for (ConstIterator i = begin(); i != end(); ++i)
497 {
498 ++count;
499 }
500 return count;
501 }
502
503 inline bool
504 empty() const noexcept
505 {
506 return first_ == nullptr;
507 }
508
509 Iterator
510 begin() noexcept
511 {
512 return Iterator(this, first_);
513 }
514
515 Iterator
516 end() noexcept
517 {
518 return Iterator(this, nullptr);
519 }
520
521 ConstIterator
522 cbegin() const noexcept
523 {
524 return ConstIterator(this, first_);
525 }
526
527 ConstIterator
528 cend() const noexcept
529 {
530 return ConstIterator(this, nullptr);
531 }
532
533 ConstIterator
534 begin() const noexcept
535 {
536 return cbegin();
537 }
538
539 ConstIterator
540 end() const noexcept
541 {
542 return cend();
543 }
544
545 /* create iterator for element */
546 Iterator
547 make_element_iterator(ElementType * element) const noexcept
548 {
549 return iterator(this, element);
550 }
551
552 /* create iterator for element */
553 ConstIterator
554 make_element_iterator(const ElementType * element) const noexcept
555 {
556 return const_iterator(this, element);
557 }
558
559 ElementType *
560 first() const noexcept
561 {
562 return first_;
563 }
564
565 ElementType *
566 last() const noexcept
567 {
568 return last_;
569 }
570
571private:
572 ElementType * first_;
573 ElementType * last_;
574
575 Accessor accessor_;
576};
577
578template<typename ElementType>
580{
581public:
582 ElementType * prev;
583 ElementType * next;
584};
585
586template<typename ElementType, IntrusiveListAnchor<ElementType> ElementType::*anchor_member>
588{
589public:
590 inline ElementType *
591 get_prev(const ElementType * element) const noexcept
592 {
593 return (element->*anchor_member).prev;
594 }
595
596 inline void
597 set_prev(ElementType * element, ElementType * prev) const noexcept
598 {
599 (element->*anchor_member).prev = prev;
600 }
601
602 inline ElementType *
603 get_next(const ElementType * element) const noexcept
604 {
605 return (element->*anchor_member).next;
606 }
607
608 inline void
609 set_next(ElementType * element, ElementType * next) const noexcept
610 {
611 (element->*anchor_member).next = next;
612 }
613};
614
615template<typename ElementType, typename Accessor>
617{
619
620public:
621 static_assert(
622 noexcept(std::declval<ElementType &>().~ElementType()),
623 "Require noexcept destructor for ElementType");
628
630 {
631 clear();
632 }
633
636
637 OwnerIntrusiveList(const OwnerIntrusiveList & other) = delete;
638
639 void
640 operator=(const OwnerIntrusiveList & other) = delete;
641
643 {
644 swap(other);
645 }
646
647 void
648 clear() noexcept
649 {
650 while (!empty())
651 {
652 ElementType * element = begin().ptr();
653 internal_list_.erase(element);
654 delete element;
655 }
656 }
657
658 void
659 swap(OwnerIntrusiveList & other) noexcept
660 {
661 internal_list_.swap(other.internal_list_);
662 }
663
664 inline void
665 push_back(std::unique_ptr<ElementType> element) noexcept
666 {
667 internal_list_.push_back(element.release());
668 }
669
670 inline void
671 push_front(std::unique_ptr<ElementType> element) noexcept
672 {
673 internal_list_.push_front(element.release());
674 }
675
676 inline Iterator
677 insert(Iterator i, std::unique_ptr<ElementType> element) noexcept
678 {
679 return internal_list_.insert(i, element.release());
680 }
681
682 inline std::unique_ptr<ElementType>
683 unlink(Iterator i) noexcept
684 {
685 ElementType * element = i.ptr();
687 return std::unique_ptr<ElementType>(element);
688 }
689
690 inline void
691 erase(ElementType * element) noexcept
692 {
694 }
695
696 inline void
697 erase(Iterator i) noexcept
698 {
699 unlink(i);
700 }
701
702 inline void
704 {
705 while (begin != end)
706 {
707 ElementType * element = begin.ptr();
708 ++begin;
709 erase(element);
710 }
711 }
712
713 inline void
714 splice(Iterator position, OwnerIntrusiveList & other) noexcept
715 {
716 splice(position, other, other.begin(), other.end());
717 }
718
719 inline void
720 splice(Iterator position, OwnerIntrusiveList & other, Iterator i) noexcept
721 {
722 Iterator j = i;
723 ++j;
724 splice(position, other, i, j);
725 }
726
727 inline void
729 {
730 internal_list_.splice(position, other.internal_list_, begin, end);
731 }
732
733 inline size_type
734 size() const noexcept
735 {
736 return internal_list_.size();
737 }
738
739 inline bool
740 empty() const noexcept
741 {
742 return internal_list_.empty();
743 }
744
746 begin() noexcept
747 {
748 return internal_list_.begin();
749 }
750
752 end() noexcept
753 {
754 return internal_list_.end();
755 }
756
758 cbegin() const noexcept
759 {
760 return internal_list_.cbegin();
761 }
762
764 cend() const noexcept
765 {
766 return internal_list_.cend();
767 }
768
770 begin() const noexcept
771 {
772 return internal_list_.begin();
773 }
774
776 end() const noexcept
777 {
778 return internal_list_.end();
779 }
780
781 /* create iterator for element */
783 make_element_iterator(ElementType * element) const noexcept
784 {
785 return Iterator(&internal_list_, element);
786 }
787
788 /* create iterator for element */
790 make_element_iterator(const ElementType * element) const noexcept
791 {
792 return ConstIterator(&internal_list_, element);
793 }
794
795private:
797};
798
799}
800
801#endif
ElementType * get_next(const ElementType *element) const noexcept
void set_next(ElementType *element, ElementType *next) const noexcept
void set_prev(ElementType *element, ElementType *prev) const noexcept
ElementType * get_prev(const ElementType *element) const noexcept
bool operator!=(const ConstIterator &other) const noexcept
const ElementType * operator->() const noexcept
std::bidirectional_iterator_tag iterator_category
ConstIterator operator++(int) noexcept
ConstIterator operator--(int) noexcept
constexpr ConstIterator(const Iterator &other) noexcept
const ConstIterator & operator--() noexcept
const ConstIterator & operator++() noexcept
const ElementType & operator*() const noexcept
constexpr ConstIterator(const IntrusiveList *list, const ElementType *object)
const ElementType * ptr() const noexcept
constexpr ConstIterator(const ConstIterator &other) noexcept=default
bool operator==(const ConstIterator &other) const noexcept
bool operator==(const Iterator &other) const noexcept
constexpr Iterator(const IntrusiveList *list, ElementType *object)
ElementType & operator*() const noexcept
ElementType * ptr() const noexcept
ElementType * operator->() const noexcept
const Iterator & operator--() noexcept
const Iterator & operator++() noexcept
std::bidirectional_iterator_tag iterator_category
bool operator!=(const Iterator &other) const noexcept
ElementType * last() const noexcept
IntrusiveList(IntrusiveList &&other) noexcept
Iterator end() noexcept
bool empty() const noexcept
ElementType * first() const noexcept
ConstIterator end() const noexcept
void erase(ElementType *element) noexcept
void splice(Iterator position, IntrusiveList &other) noexcept
size_type size() const noexcept
ConstIterator cend() const noexcept
void swap(IntrusiveList &other) noexcept
ConstIterator cbegin() const noexcept
void push_front(ElementType *element) noexcept
ConstIterator make_element_iterator(const ElementType *element) const noexcept
IntrusiveList(const IntrusiveList &other)=delete
Iterator insert(Iterator i, ElementType *element) noexcept
void erase(Iterator begin, Iterator end) noexcept
void splice(Iterator position, IntrusiveList &other, Iterator i) noexcept
void operator=(const IntrusiveList &other)=delete
Iterator erase(Iterator i) noexcept
void splice(Iterator position, IntrusiveList &other, Iterator begin, Iterator end) noexcept
ConstIterator begin() const noexcept
Iterator begin() noexcept
Iterator make_element_iterator(ElementType *element) const noexcept
constexpr IntrusiveList() noexcept
void push_back(ElementType *element) noexcept
OwnerIntrusiveList(OwnerIntrusiveList &&other) noexcept
void splice(Iterator position, OwnerIntrusiveList &other) noexcept
void swap(OwnerIntrusiveList &other) noexcept
void splice(Iterator position, OwnerIntrusiveList &other, Iterator i) noexcept
internal_list_type::ConstIterator ConstIterator
internal_list_type::size_type size_type
ConstIterator make_element_iterator(const ElementType *element) const noexcept
ConstIterator cend() const noexcept
internal_list_type::value_type value_type
void erase(ElementType *element) noexcept
ConstIterator end() const noexcept
std::unique_ptr< ElementType > unlink(Iterator i) noexcept
OwnerIntrusiveList(const OwnerIntrusiveList &other)=delete
Iterator insert(Iterator i, std::unique_ptr< ElementType > element) noexcept
ConstIterator cbegin() const noexcept
void push_back(std::unique_ptr< ElementType > element) noexcept
void erase(Iterator begin, Iterator end) noexcept
size_type size() const noexcept
void splice(Iterator position, OwnerIntrusiveList &other, Iterator begin, Iterator end) noexcept
internal_list_type::Iterator Iterator
void operator=(const OwnerIntrusiveList &other)=delete
void erase(Iterator i) noexcept
Iterator make_element_iterator(ElementType *element) const noexcept
void push_front(std::unique_ptr< ElementType > element) noexcept
IntrusiveList< ElementType, Accessor > internal_list_type
ConstIterator begin() const noexcept