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