Zth (libzth)
list.h
Go to the documentation of this file.
1 #ifndef ZTH_LIST_H
2 #define ZTH_LIST_H
3 /*
4  * Zth (libzth), a cooperative userspace multitasking library.
5  * Copyright (C) 2019-2022 Jochem Rutgers
6  *
7  * This Source Code Form is subject to the terms of the Mozilla Public
8  * License, v. 2.0. If a copy of the MPL was not distributed with this
9  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
10  */
11 
12 #ifdef __cplusplus
13 
14 # include <libzth/allocator.h>
15 # include <libzth/config.h>
16 # include <libzth/util.h>
17 
18 # include <functional>
19 
20 namespace zth {
21 
22 template <typename T>
23 class List;
24 
25 template <typename T, typename Compare = std::less<T> >
26 class SortedList;
27 
28 template <typename ChildClass>
29 class Listable {
30 public:
31  typedef ChildClass type;
32 
33  constexpr Listable() noexcept
34  : prev()
35  , next()
36  , level()
37  {}
38 
39  constexpr Listable(Listable const& e) noexcept
40  : prev()
41  , next()
42  , level()
43  {}
44 
45  Listable& operator=(Listable const& UNUSED_PAR(rhs)) noexcept
46  {
48  prev = next = nullptr;
49  return *this;
50  }
51 
52 # if __cplusplus >= 201103L
53  Listable(Listable&& l) noexcept
54  {
55  *this = std::move(l);
56  }
57 
58  Listable& operator=(Listable&& l) noexcept
59  {
60  // Cannot move while in a list.
61  zth_assert(!prev);
62  zth_assert(!l.prev);
63  zth_assert(!next);
64  zth_assert(!l.next);
65  return *this;
66  }
67 # endif
68 
69  type* listNext() const noexcept
70  {
71  zth_assert(level == 0 && next);
72  return static_cast<type*>(next);
73  }
74  type* listPrev() const noexcept
75  {
76  zth_assert(level == 0 && prev);
77  return static_cast<type*>(prev);
78  }
79 
80 private:
81  union {
82  Listable* prev; // for List
83  Listable* left; // for SortedList
84  };
85  union {
86  Listable* next; // for List
87  Listable* right; // for SortedList
88  };
89  uint_fast8_t level; // for SortedList
90 
91  friend class List<type>;
92  template <typename T_, typename Compare>
93  friend class SortedList;
94 };
95 
96 template <typename T>
97 class List {
100 public:
101  typedef T type;
103 
104  constexpr List() noexcept
105  : m_head()
106  , m_tail()
107  {}
108 
109  ~List() noexcept
110  {
112  clear();
113  }
114 
115  type& back() const noexcept
116  {
117  zth_assert(!empty());
118  return *static_cast<type*>(m_tail);
119  }
120 
121  void push_back(elem_type& elem) noexcept
122  {
123  zth_assert(elem.prev == nullptr);
124  zth_assert(elem.next == nullptr);
125 
126  if(m_tail == nullptr) {
127  zth_assert(m_head == nullptr);
128  m_tail = m_head = elem.prev = elem.next = &elem;
129  } else {
130  elem.next = m_tail->next;
131  elem.prev = m_tail;
132  m_tail = elem.next->prev = elem.prev->next = &elem;
133  }
134  }
135 
136  void pop_back() noexcept
137  {
138  zth_assert(!empty());
139 
140  elem_type* elem = m_tail;
141 
142  if(m_tail == m_head) {
143  m_tail = m_head = nullptr;
144  zth_assert(elem->prev == elem->next);
145  zth_assert(elem->prev == elem);
146  } else {
147  m_tail = m_tail->prev;
148  elem->prev->next = elem->next;
149  elem->next->prev = elem->prev;
150  }
151 
153  elem->prev = elem->next = nullptr;
154  }
155 
156  type& front() const noexcept
157  {
158  zth_assert(!empty());
159  return *static_cast<type*>(m_head);
160  }
161 
162  void push_front(elem_type& elem) noexcept
163  {
164  zth_assert(elem.prev == nullptr);
165  zth_assert(elem.next == nullptr);
166 
167  if(m_head == nullptr) {
168  zth_assert(m_tail == nullptr);
169  m_tail = m_head = elem.prev = elem.next = &elem;
170  } else {
171  elem.next = m_head;
172  elem.prev = m_head->prev;
173  elem.prev->next = elem.next->prev = m_head = &elem;
174  }
175  }
176 
177  void pop_front() noexcept
178  {
179  zth_assert(!empty());
180 
181  elem_type* elem = m_head;
182 
183  if(m_tail == m_head) {
184  m_tail = m_head = nullptr;
185  zth_assert(elem->prev == elem->next);
186  zth_assert(elem->prev == elem);
187  } else {
188  m_head = m_head->next;
189  elem->prev->next = elem->next;
190  elem->next->prev = elem->prev;
191  }
192 
194  elem->prev = elem->next = nullptr;
195  }
196 
197  bool empty() const noexcept
198  {
199  zth_assert(m_head != nullptr || m_tail == nullptr);
200  return m_head == nullptr;
201  }
202 
203  void clear() noexcept
204  {
206  while(!empty())
207  pop_front();
208  else
209  m_head = m_tail = nullptr;
210  }
211 
212  class iterator {
213  public:
214  constexpr explicit iterator(elem_type* start, elem_type* current = nullptr) noexcept
215  : m_start(start)
216  , m_current(current)
217  {}
218 
219  bool atBegin() const noexcept
220  {
221  return !m_current && m_start;
222  }
223 
224  bool atEnd() const noexcept
225  {
226  return m_current == m_start;
227  }
228 
229  elem_type* get() const noexcept
230  {
231  return atBegin() ? m_start : m_current;
232  }
233 
234  bool operator==(iterator const& rhs) const noexcept
235  {
236  return m_current == rhs.m_current;
237  }
238 
239  bool operator!=(iterator const& rhs) const noexcept
240  {
241  return !(*this == rhs);
242  }
243 
244  void next() noexcept
245  {
246  zth_assert(!atEnd());
247  m_current = atBegin() ? m_start->next : m_current->next;
248  }
249 
250  void prev() noexcept
251  {
252  zth_assert(!atBegin());
253  zth_assert(m_start);
254  m_current = m_current->prev == m_start ? nullptr : m_current->prev;
255  }
256 
257  iterator& operator++() noexcept
258  {
259  next();
260  return *this;
261  }
262 
263  iterator operator++(int) noexcept
264  {
265  iterator i = *this;
266  next();
267  return i;
268  }
269 
270  iterator& operator--() noexcept
271  {
272  prev();
273  return *this;
274  }
275 
276  iterator operator--(int) noexcept
277  {
278  iterator i = *this;
279  prev();
280  return i;
281  }
282 
283  type& operator*() const noexcept
284  {
285  elem_type* elem = get();
286  zth_assert(elem);
287  // cppcheck-suppress nullPointerRedundantCheck
288  return *static_cast<type*>(elem);
289  }
290 
291  type* operator->() const noexcept
292  {
293  return &this->operator*();
294  }
295 
296  private:
297  elem_type* m_start;
298  elem_type* m_current;
299  };
300 
301  iterator begin() const noexcept
302  {
303  return iterator(m_head);
304  }
305 
306  iterator end() const noexcept
307  {
308  return iterator(m_head, m_head);
309  }
310 
311  bool contains(elem_type& elem) const noexcept
312  {
313  for(iterator it = begin(); it != end(); ++it)
314  if(it.get() == &elem)
315  return true;
316  return false;
317  }
318 
319  size_t size() const noexcept
320  {
321  size_t res = 0;
322  for(iterator it = begin(); it != end(); ++it)
323  res++;
324  return res;
325  }
326 
327  iterator insert(iterator const& pos, elem_type& elem) noexcept
328  {
329  if(pos.atEnd()) {
330  push_back(elem);
331  return iterator(m_head, &elem);
332  } else if(pos.atBegin()) {
333  push_front(elem);
334  return begin();
335  } else {
336  elem_type* before = pos.get();
337  elem->next = before;
338  elem->prev = before->prev;
339  before->prev = elem->prev->next = elem;
340  return iterator(m_head, &elem);
341  }
342  }
343 
344  iterator erase(iterator const& pos) noexcept
345  {
346  zth_assert(!pos.atEnd());
347  if(pos.atBegin()) {
348  erase(*pos.get());
349  return begin();
350  } else if(pos.get() == m_tail) {
351  erase(*pos.get());
352  return end();
353  } else {
354  elem_type* next = pos.get()->next;
355  erase(*pos.get());
356  return iterator(m_head, next);
357  }
358  }
359 
360  void erase(elem_type& elem) noexcept
361  {
362  zth_assert(contains(elem));
363  if(m_head == &elem) {
364  pop_front();
365  } else if(m_tail == &elem) {
366  pop_back();
367  } else {
368  elem.prev->next = elem.next;
369  elem.next->prev = elem.prev;
371  elem.next = elem.prev = nullptr;
372  }
373  }
374 
375  bool contains(type& elem) const noexcept
376  {
377  for(iterator it = begin(); it != end(); ++it)
378  if(it.get() == &elem)
379  return true;
380  return false;
381  }
382 
383  void rotate(elem_type& elem) noexcept
384  {
385  zth_assert(contains(elem));
386  m_head = &elem;
387  m_tail = elem.prev;
388  }
389 
390 private:
391  elem_type* m_head;
392  elem_type* m_tail;
393 };
394 
395 template <typename T, typename Compare>
396 class SortedList {
399 public:
400  typedef T type;
402 
403  constexpr SortedList() noexcept
404  : m_comp()
405  , m_t()
406  , m_head()
407  , m_size()
408  {}
409 
410  ~SortedList() noexcept
411  {
412  clear();
413  }
414 
415  void insert(elem_type& x) noexcept
416  {
417  zth_assert(!x.left);
418  zth_assert(!x.right);
419  zth_assert(x.level == 0);
420  zth_assert(!contains(x));
421  if(!m_t)
422  m_head = &x;
423  m_t = insert_(x, m_t);
424  m_size++;
425  check();
426  }
427 
428  void erase(elem_type& x) noexcept
429  {
430  zth_assert(contains(x));
431  m_t = delete_(x, m_t);
432  if(m_head == &x)
433  m_head = lowest(m_t);
435  x.level = 0;
436  x.left = x.right = nullptr;
437  }
438  m_size--;
439  check();
440  }
441 
442  void clear() noexcept
443  {
445  while(!empty())
446  erase(*m_t);
447  } else {
448  m_t = m_head = nullptr;
449  m_size = 0;
450  }
451  }
452 
453  size_t size() const noexcept
454  {
455  zth_assert(m_size > 0 || !m_t);
456  return m_size;
457  }
458 
459  bool empty() const noexcept
460  {
461  zth_assert(m_size > 0 || !m_t);
462  return !m_t;
463  }
464 
465  bool contains(elem_type& x) const noexcept
466  {
467  elem_type* t = m_t;
468  while(t) {
469  if(t == &x)
470  return true;
471  else if(m_comp(static_cast<type&>(x), static_cast<type&>(*t)))
472  t = t->left;
473  else if(m_comp(static_cast<type&>(*t), static_cast<type&>(x)))
474  t = t->right;
475  else if((uintptr_t)&x < (uintptr_t)t)
476  t = t->left;
477  else
478  t = t->right;
479  }
480  return false;
481  }
482 
483  type& front() const noexcept
484  {
485  zth_assert(m_head && m_t);
486  return static_cast<type&>(*m_head);
487  }
488 
489 protected:
490  static elem_type* skew(elem_type* t) noexcept
491  {
492  if(!t)
493  return nullptr;
494  else if(!t->left)
495  return t;
496  else if(t->left->level == t->level) {
497  elem_type* l = t->left;
498  t->left = l->right;
499  l->right = t;
500  return l;
501  } else
502  return t;
503  }
504 
505  static elem_type* split(elem_type* t) noexcept
506  {
507  if(!t)
508  return nullptr;
509  else if(!t->right || !t->right->right)
510  return t;
511  else if(t->level == t->right->right->level) {
512  elem_type* r = t->right;
513  t->right = r->left;
514  r->left = t;
515  zth_assert(r->level < std::numeric_limits<decltype(r->level)>::max());
516  r->level++;
517  return r;
518  } else
519  return t;
520  }
521 
523  {
524  if(!t) {
525  x.level = 1;
526  x.left = x.right = nullptr;
527  return &x;
528  } else if(m_comp(static_cast<type&>(x), static_cast<type&>(*t)))
529  t->left = insert_(x, t->left);
530  else if(m_comp(static_cast<type&>(*t), static_cast<type&>(x)))
531  t->right = insert_(x, t->right);
532  else if((uintptr_t)&x < (uintptr_t)t)
533  t->left = insert_(x, t->left);
534  else
535  t->right = insert_(x, t->right);
536 
537  if(t == m_head && t->left)
538  m_head = t->left;
539 
540  return split(skew(t));
541  }
542 
544  {
545  if(!t)
546  return nullptr;
547  else if(&x == t) {
548  if(!t->left && !t->right) {
549  return nullptr;
550  } else if(!t->left) {
551  elem_type* l = successor(t);
552  l->right = delete_(*l, t->right);
553  l->left = t->left;
554  l->level = t->level;
555  t = l;
556  } else {
557  elem_type* l = predecessor(t);
558  l->left = delete_(*l, t->left);
559  l->right = t->right;
560  l->level = t->level;
561  t = l;
562  }
563  } else if(m_comp(static_cast<type&>(x), static_cast<type&>(*t)))
564  t->left = delete_(x, t->left);
565  else if(m_comp(static_cast<type&>(*t), static_cast<type&>(x)))
566  t->right = delete_(x, t->right);
567  else if((uintptr_t)&x < (uintptr_t)t)
568  t->left = delete_(x, t->left);
569  else
570  t->right = delete_(x, t->right);
571 
572  decrease_level(t);
573  t = skew(t);
574  t->right = skew(t->right);
575  if(t->right)
576  t->right->right = skew(t->right->right);
577  t = split(t);
578  t->right = split(t->right);
579  return t;
580  }
581 
582  static elem_type* lowest(elem_type* t) noexcept
583  {
584  if(!t)
585  return nullptr;
586  else if(!t->left)
587  return t;
588  else
589  return lowest(t->left);
590  }
591 
592  static elem_type* highest(elem_type* t) noexcept
593  {
594  if(!t)
595  return nullptr;
596  else if(!t->right)
597  return t;
598  else
599  return highest(t->right);
600  }
601 
602  static elem_type* successor(elem_type* t) noexcept
603  {
604  return t ? lowest(t->right) : nullptr;
605  }
606 
607  static elem_type* predecessor(elem_type* t) noexcept
608  {
609  return t ? highest(t->left) : nullptr;
610  }
611 
612  static void decrease_level(elem_type* t) noexcept
613  {
614 #ifndef CPPCHECK // Internal error, somehow.
615  decltype(t->level) ll = t->left ? t->left->level : 0;
616  decltype(t->level) lr = t->right ? t->right->level : 0;
617  decltype(t->level) should_be = (decltype(t->level))((ll < lr ? ll : lr) + 1u);
618  if(should_be < t->level) {
619  t->level = should_be;
620  if(t->right && should_be < t->right->level)
621  t->right->level = should_be;
622  }
623 #endif
624  }
625 
626  void check() const noexcept
627  {
628  zth_dbg(list, "SortedList %p (head %p):", this, m_head);
629  print(m_t, 2);
630  zth_assert(m_size == check(m_t));
631  zth_assert((!m_t && !m_head) || (m_t && m_head && lowest(m_t) == m_head));
632  }
633 
634  size_t check(elem_type* t) const noexcept
635  {
637  return 0;
638  if(!t)
639  return 0;
640 
641  zth_assert(
642  !t->left || m_comp(static_cast<type&>(*t->left), static_cast<type&>(*t))
643  || (!m_comp(static_cast<type&>(*t), static_cast<type&>(*t->left))
644  && ((uintptr_t)t > (uintptr_t)t->left)));
645  zth_assert(
646  !t->right || m_comp(static_cast<type&>(*t), static_cast<type&>(*t->right))
647  || (!m_comp(static_cast<type&>(*t->right), static_cast<type&>(*t))
648  || ((uintptr_t)t < (uintptr_t)t->right)));
649 
650  zth_assert(t->left || t->right || t->level == 1);
651  zth_assert(!t->left || t->left->level == t->level - 1);
652  zth_assert(
653  !t->right || t->right->level == t->level - 1
654  || t->right->level == t->level);
655  zth_assert(!t->right || !t->right->right || t->right->right->level < t->level);
656  zth_assert(t->level < 2 || (t->left && t->right));
657  return 1 + check(t->left) + check(t->right);
658  }
659 
660  static void print(elem_type* t, int indent = 0) noexcept
661  {
663  return;
664 
665  if(!t)
666  zth_dbg(list, "%*s (null)", indent, "");
667  else {
668  zth_dbg(list, "%*s node %p level=%d: %s", indent, "", static_cast<type*>(t),
669  t->level, static_cast<type*>(t)->str().c_str());
670  if(t->left || t->right) {
671  print(t->left, indent + 2);
672  print(t->right, indent + 2);
673  }
674  }
675  }
676 
677 private:
678  Compare m_comp;
679  elem_type* m_t;
680  elem_type* m_head;
681  size_t m_size;
682 };
683 
684 } // namespace zth
685 #endif // __cplusplus
686 #endif // ZTH_LIST_H
bool operator==(iterator const &rhs) const noexcept
Definition: list.h:234
iterator operator--(int) noexcept
Definition: list.h:276
type * operator->() const noexcept
Definition: list.h:291
bool atEnd() const noexcept
Definition: list.h:224
iterator & operator++() noexcept
Definition: list.h:257
bool operator!=(iterator const &rhs) const noexcept
Definition: list.h:239
constexpr iterator(elem_type *start, elem_type *current=nullptr) noexcept
Definition: list.h:214
elem_type * get() const noexcept
Definition: list.h:229
bool atBegin() const noexcept
Definition: list.h:219
void next() noexcept
Definition: list.h:244
type & operator*() const noexcept
Definition: list.h:283
iterator & operator--() noexcept
Definition: list.h:270
iterator operator++(int) noexcept
Definition: list.h:263
void prev() noexcept
Definition: list.h:250
Definition: list.h:97
T type
Definition: list.h:101
iterator begin() const noexcept
Definition: list.h:301
size_t size() const noexcept
Definition: list.h:319
void erase(elem_type &elem) noexcept
Definition: list.h:360
bool contains(type &elem) const noexcept
Definition: list.h:375
bool empty() const noexcept
Definition: list.h:197
iterator erase(iterator const &pos) noexcept
Definition: list.h:344
bool contains(elem_type &elem) const noexcept
Definition: list.h:311
type & back() const noexcept
Definition: list.h:115
type & front() const noexcept
Definition: list.h:156
constexpr List() noexcept
Definition: list.h:104
void push_back(elem_type &elem) noexcept
Definition: list.h:121
iterator insert(iterator const &pos, elem_type &elem) noexcept
Definition: list.h:327
void push_front(elem_type &elem) noexcept
Definition: list.h:162
iterator end() const noexcept
Definition: list.h:306
void clear() noexcept
Definition: list.h:203
~List() noexcept
Definition: list.h:109
void pop_front() noexcept
Definition: list.h:177
Listable< type > elem_type
Definition: list.h:102
void pop_back() noexcept
Definition: list.h:136
void rotate(elem_type &elem) noexcept
Definition: list.h:383
type * listNext() const noexcept
Definition: list.h:69
Listable * right
Definition: list.h:87
ChildClass type
Definition: list.h:31
Listable & operator=(Listable &&l) noexcept
Definition: list.h:58
constexpr Listable() noexcept
Definition: list.h:33
Listable * next
Definition: list.h:86
type * listPrev() const noexcept
Definition: list.h:74
Listable * left
Definition: list.h:83
Listable * prev
Definition: list.h:82
Listable & operator=(Listable const &rhs) noexcept
Definition: list.h:45
constexpr Listable(Listable const &e) noexcept
Definition: list.h:39
Listable(Listable &&l) noexcept
Definition: list.h:53
void erase(elem_type &x) noexcept
Definition: list.h:428
void check() const noexcept
Definition: list.h:626
void clear() noexcept
Definition: list.h:442
void insert(elem_type &x) noexcept
Definition: list.h:415
bool contains(elem_type &x) const noexcept
Definition: list.h:465
static void decrease_level(elem_type *t) noexcept
Definition: list.h:612
static elem_type * predecessor(elem_type *t) noexcept
Definition: list.h:607
type & front() const noexcept
Definition: list.h:483
size_t size() const noexcept
Definition: list.h:453
elem_type * insert_(elem_type &x, elem_type *t) noexcept
Definition: list.h:522
static elem_type * successor(elem_type *t) noexcept
Definition: list.h:602
static void print(elem_type *t, int indent=0) noexcept
Definition: list.h:660
elem_type * delete_(elem_type &x, elem_type *t) noexcept
Definition: list.h:543
static elem_type * highest(elem_type *t) noexcept
Definition: list.h:592
~SortedList() noexcept
Definition: list.h:410
size_t check(elem_type *t) const noexcept
Definition: list.h:634
static elem_type * skew(elem_type *t) noexcept
Definition: list.h:490
constexpr SortedList() noexcept
Definition: list.h:403
static elem_type * lowest(elem_type *t) noexcept
Definition: list.h:582
bool empty() const noexcept
Definition: list.h:459
Listable< type > elem_type
Definition: list.h:401
static elem_type * split(elem_type *t) noexcept
Definition: list.h:505
#define zth_dbg(group, fmt, a...)
Debug printf()-like function.
Definition: util.h:210
#define ZTH_CLASS_NEW_DELETE(T)
Define new/delete operators for a class, which are allocator-aware.
Definition: allocator.h:114
#define UNUSED_PAR(name)
Definition: macros.h:79
Definition: allocator.h:23
static bool const EnableAssert
When true, enable zth_assert().
Definition: config.h:58
static int const Print_list
Definition: config.h:127
static bool const SupportDebugPrint
Add support to enable debug output prints.
Definition: config.h:103
#define zth_assert(expr)
assert(), but better integrated in Zth.
Definition: util.h:236
#define ZTH_CLASS_NOCOPY(Class)
Definition: util.h:254