Jlm
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 
85 namespace jlm::util
86 {
87 
88 template<typename ElementType, typename Accessor>
90 {
91 public:
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 
98  class Iterator
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 
125  Iterator
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  {
142  element_ = list_->last_;
143  }
144  return *this;
145  }
146 
147  Iterator
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  {
242  element_ = list_->last_;
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 
571 private:
572  ElementType * first_;
573  ElementType * last_;
574 
575  Accessor accessor_;
576 };
577 
578 template<typename ElementType>
580 {
581 public:
582  ElementType * prev;
583  ElementType * next;
584 };
585 
586 template<typename ElementType, IntrusiveListAnchor<ElementType> ElementType::*anchor_member>
588 {
589 public:
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 
615 template<typename ElementType, typename Accessor>
617 {
619 
620 public:
621  static_assert(
622  noexcept(std::declval<ElementType &>().~ElementType()),
623  "Require noexcept destructor for ElementType");
628 
630  {
631  clear();
632  }
633 
635  {}
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  {
693  erase(make_element_iterator(element));
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 
745  Iterator
746  begin() noexcept
747  {
748  return internal_list_.begin();
749  }
750 
751  Iterator
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 */
782  Iterator
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 
795 private:
797 };
798 
799 }
800 
801 #endif
ElementType * get_prev(const ElementType *element) const noexcept
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
bool operator!=(const ConstIterator &other) const noexcept
std::bidirectional_iterator_tag iterator_category
ConstIterator operator++(int) noexcept
ConstIterator operator--(int) noexcept
const ElementType * ptr() const noexcept
const ElementType & operator*() const noexcept
const ElementType * operator->() const noexcept
constexpr ConstIterator(const Iterator &other) noexcept
const ConstIterator & operator--() noexcept
const ConstIterator & operator++() noexcept
constexpr ConstIterator(const IntrusiveList *list, const ElementType *object)
constexpr ConstIterator(const ConstIterator &other) noexcept=default
bool operator==(const ConstIterator &other) const noexcept
ElementType * ptr() const noexcept
bool operator==(const Iterator &other) const noexcept
constexpr Iterator(const IntrusiveList *list, ElementType *object)
ElementType * operator->() const noexcept
const Iterator & operator--() noexcept
const Iterator & operator++() noexcept
ElementType & operator*() const noexcept
std::bidirectional_iterator_tag iterator_category
bool operator!=(const Iterator &other) const noexcept
IntrusiveList(IntrusiveList &&other) noexcept
Iterator end() noexcept
bool empty() 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
ElementType * last() 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
ElementType * first() 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 internal_list_
internal_list_type::ConstIterator ConstIterator
internal_list_type::size_type size_type
std::unique_ptr< ElementType > unlink(Iterator i) noexcept
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
bool empty() const 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