mCRL2
Loading...
Searching...
No Matches
simple_list.h
Go to the documentation of this file.
1// Author(s): David N. Jansen, Institute of Software, Chinese Academy of
2// Sciences, Beijing, PR China
3//
4// Copyright: see the accompanying file COPYING or copy at
5// https://github.com/mCRL2org/mCRL2/blob/master/COPYING
6//
7// Distributed under the Boost Software License, Version 1.0.
8// (See accompanying file LICENSE_1_0.txt or copy at
9// http://www.boost.org/LICENSE_1_0.txt)
10
42
43#ifndef SIMPLE_LIST_H
44#define SIMPLE_LIST_H
45
46#include <cstddef> // for std::size_t
47#include <new> // for placement new
48#include <type_traits> // for std::is_trivially_destructible<class>
49#include <functional> // for std::less
50#include <iterator> // for std::forward_iterator_tag
51
52// My provisional recommendation is to always use simple lists and pool
53// allocators. Using standard allocation and standard lists is 5-15% slower
54// and uses perhaps 0.7% more memory. Using standard allocation and simple
55// lists is 10-20% slower and has no significant effect on memory use. These
56// numbers are based on a small set with not-so-large case studies for the JGKW
57// bisimulation minimisation algorithm, none of which includes new bottom
58// states.
59
60#define USE_SIMPLE_LIST
61
62#ifndef USE_SIMPLE_LIST
63 #include <list>
64#endif
65
66#define USE_POOL_ALLOCATOR
67
68namespace mcrl2
69{
70namespace lts
71{
72namespace detail
73{
74
75
76
77
78
79/* ************************************************************************* */
80/* */
81/* M E M O R Y M A N A G E M E N T */
82/* */
83/* ************************************************************************* */
84
85
86
87
88
89#ifdef USE_POOL_ALLOCATOR
90 #define ONLY_IF_POOL_ALLOCATOR(...) __VA_ARGS__
91 #ifndef USE_SIMPLE_LIST
92 #error "Using the pool allocator also requires using the simple list"
93 #endif
94
110 template <class T, std::size_t BLOCKSIZE = 4000>
112 { static_assert(std::is_trivially_destructible<T>::value);
113 private: static_assert(sizeof(void*) <= sizeof(T));
115 {
116 public:
117 char data[BLOCKSIZE - sizeof(pool_block_t*)];
119
120 pool_block_t(pool_block_t* const new_next_block)
121 : next_block(new_next_block)
122 { }
123 }; static_assert(sizeof(T) <= sizeof(pool_block_t::data));
124
128
131
134
135 static void*& deref_void(void* addr)
136 {
137 return *static_cast<void**>(addr);
138 }
139 public:
142 : first_block(new pool_block_t(nullptr)),
144 &first_block->data[sizeof(first_block->data)]),
145 first_free_T(nullptr)
146 { }
147
148
151 {
152 pool_block_t* block(first_block); assert(nullptr != block);
153 do
154 {
155 pool_block_t* next_block = block->next_block;
156 delete block;
157 block = next_block;
158 }
159 while(nullptr != block);
160 }
161
162
163 private:
166 template <class U, class... Args>
167 U* construct_samesize(Args&&... args)
168 { static_assert(sizeof(T) == sizeof(U));
169 void* new_el; assert(nullptr != first_block);
170 if (first_block->data + sizeof(U) <= begin_used_in_first_block)
171 {
173 new_el = static_cast<char*>(begin_used_in_first_block) -
174 sizeof(U);
175 }
176 else if (nullptr != first_free_T)
177 {
178 // free list is tested afterwards because I expect that there
179 // won't be too many elements in the free list.
180 new_el = first_free_T;
181 first_free_T = deref_void(new_el);
182 }
183 else
184 {
187 new_el = &first_block->data[sizeof(first_block->data) -
188 sizeof(U)];
189 }
190 return new(new_el) U(std::forward<Args>(args)...);
191 }
192
193
196 template <class U, class... Args>
197 U* construct_othersize(Args&&... args)
198 { static_assert(sizeof(U) != sizeof(T));
199 void* new_el; assert(nullptr != first_block);
200 if (first_block->data + sizeof(U) <= begin_used_in_first_block)
201 {
203 new_el = static_cast<char*>(begin_used_in_first_block) -
204 sizeof(U);
205 }
206 else
207 {
208 if constexpr (sizeof(T) * 2 < sizeof(U))
209 {
210 // There may be space for several T-elements,
211 // extend the free list accordingly
212 while (first_block->data + sizeof(T) <=
214 {
215 begin_used_in_first_block = static_cast<char*>
216 (begin_used_in_first_block) - sizeof(T);
219 }
220 }
221 else if constexpr (sizeof(T) < sizeof(U))
222 {
223 // There may be space for one T-element (but not more),
224 // extend the free list accordingly
225 if (first_block->data + sizeof(T) <=
227 {
228 begin_used_in_first_block = static_cast<char*>
229 (begin_used_in_first_block) - sizeof(T);
232 }
233 } assert(first_block->data + sizeof(T) > begin_used_in_first_block);
236 new_el = &first_block->data[sizeof(first_block->data) -
237 sizeof(U)];
238 }
239 return new(new_el) U(std::forward<Args>(args)...);
240 }
241 public:
243 template <class U, class... Args>
244 U* construct(Args&&... args)
245 { static_assert(std::is_trivially_destructible<U>::value);
246 if constexpr (sizeof(U) == sizeof(T))
247 {
248 return construct_samesize<U>(std::forward<Args>(args)...);
249 }
250 else
251 { static_assert(sizeof(U) <= sizeof(first_block->data));
252 return construct_othersize<U>(std::forward<Args>(args)...);
253 }
254 }
255
256
263 template <class U>
264 void destroy(U* const old_el)
265 { static_assert(sizeof(T) == sizeof(U));
266 old_el->~U(); static_assert(std::is_trivially_destructible<U>::value);
267 #ifndef NDEBUG
268 // ensure that old_el points to an element in some block
269 static std::less<const void*> const total_order;
270 for (const pool_block_t* block(first_block);
271 assert(nullptr != block),
272 total_order(old_el, block->data) ||
273 total_order(&block->data[sizeof(block->data)], old_el + 1);
274 block = block->next_block )
275 { }
276 #endif
277 deref_void(old_el) = first_free_T;
278 first_free_T = static_cast<void*>(old_el);
279 }
280 };
281#else
282 #define ONLY_IF_POOL_ALLOCATOR(...)
283#endif // #define USE_POOL_ALLOCATOR
284
285#ifdef USE_SIMPLE_LIST
294 template <class T>
296 {
297 public:
298 class const_iterator;
299
300 #ifndef USE_POOL_ALLOCATOR
301 private:
302 #endif
306 class entry
307 {
308 private:
312
313 friend class simple_list;
314 friend class const_iterator;
315 friend class my_pool<entry>;
316
317 template <class... Args>
318 entry(entry* const new_next, entry* const new_prev, Args&&... args)
319 : next(new_next),
320 prev(new_prev),
321 data(std::forward<Args>(args)...)
322 { }
323 };
324
325 private:
328
329 public:
330 #ifdef USE_POOL_ALLOCATOR
332 {
333 static my_pool<entry> pool;
334
335 return pool;
336 }
337 #endif
338
341 {
342 public:
343 typedef T value_type;
344 typedef T* pointer;
345 typedef T& reference;
346 typedef std::ptrdiff_t difference_type;
347 typedef std::forward_iterator_tag iterator_category;
348 protected:
350
351 const_iterator(const entry* const new_ptr)
352 : ptr(const_cast<entry*>(new_ptr))
353 { }
354
355 friend class simple_list;
356 public:
357 const_iterator() = default;
358 const_iterator(const const_iterator& other) = default;
359 const_iterator& operator=(const const_iterator& other) = default;
361 { assert(nullptr != ptr);
362 ptr = ptr->next;
363 return *this;
364 }
366 { assert(nullptr != ptr);
367 ptr = ptr->prev; assert(nullptr != ptr->next);
368 return *this;
369 }
370 const T& operator*() const
371 { assert(nullptr != ptr);
372 return ptr->data;
373 }
374 const T* operator->() const
375 { assert(nullptr != ptr);
376 return &ptr->data;
377 }
378 bool operator==(const const_iterator& other) const
379 {
380 return ptr == other.ptr;
381 }
382 bool operator!=(const const_iterator& other) const
383 {
384 return !operator==(other);
385 }
386 };
387
390 {
391 public:
397 using const_iterator::operator==;
398 using const_iterator::operator!=;
399 protected:
400 iterator(entry* const new_ptr) : const_iterator(new_ptr) { }
401
402 friend class simple_list;
403 public:
404 iterator() = default;
405
406 iterator(const iterator& other) = default;
407
408 iterator& operator=(const iterator& other) = default;
409
411
413
414 T& operator*() const
415 {
416 return const_cast<T&>(const_iterator::operator*());
417 }
418
419 T* operator->() const
420 {
421 return const_cast<T*>(const_iterator::operator->());
422 }
423 };
424
430 {
431 public:
432 using typename iterator::value_type;
433 using typename iterator::pointer;
434 using typename iterator::reference;
437 using iterator::operator*;
438 using iterator::operator->;
439 using iterator::operator==;
440 using iterator::operator!=;
441
443
444 iterator_or_null(std::nullptr_t) : iterator()
445 {
446 const_iterator::ptr = nullptr;
447 }
448
449 iterator_or_null(const iterator& other) : iterator(other) { }
450
451 bool is_null() const { return nullptr == const_iterator::ptr; }
452
453 bool operator==(const T* const other) const
454 { assert(nullptr != other);
455 // It is allowed to call this even if is_null().
456 return !is_null() && operator->() == other;
457 }
458
459 bool operator!=(const T* const other) const
460 {
461 return !operator==(other);
462 }
463
464 void operator=(std::nullptr_t)
465 {
466 const_iterator::ptr = nullptr;
467 }
468 };
469
472 : first(nullptr)
473 { static_assert(std::is_trivially_destructible<entry>::value);
474 }
475
476 #ifndef USE_POOL_ALLOCATOR
479 {
480 for (iterator iter = begin(); end() != iter; )
481 {
482 iterator next = std::next(iter);
483 delete iter.ptr;
484 iter = next;
485 }
486 }
487 #endif
488
490 bool empty() const { return nullptr==first; }
491
494
496 static iterator end() { return iterator(nullptr); }
497
500
502 static const_iterator cend() { return end(); }
503
505 const_iterator begin() const { return cbegin(); }
506
509 { assert(!empty());
510 return iterator(first->prev);
511 }
512
514 { assert(!empty());
515 return const_iterator(first->prev);
516 }
517
519 T& front()
520 { assert(!empty());
521 return first->data;
522 }
523
525 T& back()
526 { assert(!empty());
527 return first->prev->data;
528 }
529 #ifndef NDEBUG
530 [[nodiscard]]
531 bool check_linked_list() const
532 {
533 if (empty())
534 {
535 return true;
536 }
538 if (nullptr == i.ptr->prev)
539 {
540 return false;
541 }
542 while (nullptr != i.ptr->next)
543 {
544 if (i.ptr->next->prev != i.ptr)
545 {
546 return false;
547 }
548 ++i;
549 assert(i.ptr->prev->next == i.ptr);
550 }
551 return first->prev == i.ptr;
552 }
553 #endif
556 template<class... Args>
557 iterator emplace(iterator pos, Args&&... args)
558 { assert(end()==pos || !empty()); assert(end()==pos || nullptr==pos.ptr->prev);
559 #ifndef NDEBUG
560 assert(check_linked_list());
561 if (end() != pos)
562 { for(const_iterator i=begin(); i!=pos; ++i) { assert(end()!=i); } }
563 #endif
564 entry* const prev = end() == pos
565 ? (empty() ? nullptr : first->prev)
566 : pos.ptr->prev;
567 entry* const new_entry(
568 #ifdef USE_POOL_ALLOCATOR
569 get_pool().template construct<entry>
570 #else
571 new entry
572 #endif
573 (pos.ptr, prev, std::forward<Args>(args)...));
574 if (begin() == pos)
575 {
576 // we insert a new element before the current list begin, so
577 // the begin should change. This includes the case that the
578 // list was empty before.
579 first = new_entry;
580 }
581 else if (nullptr != prev)
582 {
583 // We insert an element not at the current list begin, so it
584 // should be reachable from its predecessor.
585 prev->next = new_entry;
586 }
587 if (end() != pos)
588 {
589 pos.ptr->prev = new_entry;
590 }
591 else
592 { assert(nullptr != first);
593 first->prev = new_entry;
594 } assert(check_linked_list());
595 #ifndef NDEBUG
596 assert((end() == pos ? before_end() : pos.ptr->prev) == new_entry);
597 for (const_iterator i=begin(); i != new_entry; ++i) { assert(end() != i); }
598 #endif
599 return iterator(new_entry);
600 }
601
602
604 template <class... Args>
605 iterator emplace_back(Args&&... args)
606 {
607 return emplace(end(), std::forward<Args>(args)...);
608 }
609
610
613 template<class... Args>
614 iterator emplace_after(iterator pos, Args&&... args)
615 { assert(end()==pos || !empty()); assert(end()==pos || end()!=pos.ptr->prev);
616 #ifndef NDEBUG
617 assert(check_linked_list());
618 if (end() != pos) {
619 for (const_iterator i = begin(); i != pos; ++i) { assert(end() != i); }
620 }
621 #endif
622 entry* const next = end() == pos ? begin().ptr : pos.ptr->next;
623 entry* const new_entry(
624 #ifdef USE_POOL_ALLOCATOR
625 get_pool().template construct<entry>
626 #else
627 new entry
628 #endif
629 (next, pos.ptr, std::forward<Args>(args)...));
630 if (end() == pos)
631 {
632 // we insert a new element before the current list begin, so
633 // the begin should change. This includes the case that the
634 // list was empty before.
635 new_entry->prev = empty() ? new_entry : before_end().ptr;
636 first = new_entry;
637 }
638 else
639 {
640 pos.ptr->next = new_entry;
641 } assert(nullptr != first);
642 if (nullptr == next)
643 {
644 first->prev = new_entry;
645 }
646 else
647 {
648 next->prev = new_entry;
649 } assert(check_linked_list());
650 #ifndef NDEBUG
651 assert((end() == pos ? first : pos.ptr->next) == new_entry);
652 for (const_iterator i=begin(); i != new_entry; ++i) { assert(end() != i); }
653 #endif
654 return iterator(new_entry);
655 }
656
657
659 template<class... Args>
660 iterator emplace_front(Args&&... args)
661 {
662 return emplace_after(end(), std::forward<Args>(args)...);
663 }
664
665
670 void splice_to_after(iterator const to_pos, simple_list<T>& from_list,
671 iterator const from_pos)
672 { assert(from_pos != to_pos);
673 #ifndef NDEBUG
674 assert(check_linked_list());
675 if (end() != to_pos) {
676 assert(!empty()); assert(nullptr != to_pos.ptr->prev);
677 for(const_iterator i = begin(); i!=to_pos; ++i) { assert(end() != i); }
678 }
679 assert(end() != from_pos); assert(nullptr != from_pos.ptr->prev);
680 assert(!from_list.empty()); assert(from_list.check_linked_list());
681 /* remove element from_pos from its original list */ for (const_iterator i = from_list.begin(); i != from_pos; ++i) {
682 assert(from_list.end() != i);
683 }
684 #endif
685 if (from_pos.ptr != from_list.first)
686 { assert(nullptr != from_list.first->next); // at least 2 elements in the list
687 /* not the first element in from_list */ assert(from_pos == from_pos.ptr->prev->next);
688 from_pos.ptr->prev->next = from_pos.ptr->next;
689 if (nullptr != from_pos.ptr->next)
690 {
691 /* not the last element in from_list */ assert(from_pos == from_pos.ptr->next->prev);
692 from_pos.ptr->next->prev = from_pos.ptr->prev;
693 }
694 else
695 {
696 /* last element in from_list */ assert(from_pos == from_list.first->prev);
697 from_list.first->prev = from_pos.ptr->prev;
698 }
699 }
700 else
701 {
702 /* first element in from_list */ assert(nullptr == from_pos.ptr->prev->next);
703 from_list.first = from_pos.ptr->next;
704 if (!from_list.empty())
705 {
706 /* not the last element in from_list */ assert(from_pos == from_pos.ptr->next->prev);
707 from_pos.ptr->next->prev = from_pos.ptr->prev;
708 }
709 }
710 // update the pointers of from_pos and insert from_pos into this
711 // list
712 entry* next;
713 if (end() == to_pos)
714 {
715 // we insert the element before the current list begin, so
716 // the begin should change. This includes the case that the
717 // list was empty before.
718 if (!empty())
719 {
720 from_pos.ptr->prev = before_end().ptr;
721 }
722 // else from_pos->prev = from_pos; -- will be set below.
723 next = first;
724 first = from_pos.ptr;
725 }
726 else
727 {
728 from_pos.ptr->prev = to_pos.ptr;
729 next = to_pos.ptr->next;
730 to_pos.ptr->next = from_pos.ptr;
731 } assert(nullptr != first);
732 from_pos.ptr->next = next;
733 if (nullptr == next)
734 {
735 first->prev = from_pos.ptr;
736 }
737 else
738 {
739 next->prev = from_pos.ptr;
740 } assert(check_linked_list()); assert(from_list.check_linked_list());
741 #ifndef NDEBUG
742 assert(from_pos == (end() == to_pos ? first : to_pos.ptr->next));
743 for (const_iterator i=begin(); i!=from_pos; ++i) { assert(i!=end()); }
744 if (end() != to_pos) {
745 for (const_iterator i=begin(); i!=to_pos; ++i) { assert(end() != i); }
746 }
747 #endif
748 }
749
750
757 void splice(iterator const to_pos, simple_list<T>& from_list,
758 iterator const from_pos)
759 { assert(from_pos != to_pos); assert(end() == to_pos || !empty());
760 #ifndef NDEBUG
761 assert(end() == to_pos || nullptr != to_pos.ptr->prev);
762 assert(check_linked_list());
763 if (end() != to_pos) {
764 for (const_iterator i=begin(); i!=to_pos; ++i) { assert(end() != i); }
765 }
766 assert(from_list.end() != from_pos); assert(nullptr != from_pos.ptr->prev);
767 assert(!from_list.empty()); assert(from_list.check_linked_list());
768 /* remove element from_pos from its original list */ for (const_iterator i=from_list.begin(); i!=from_pos; ++i) {
769 assert(from_list.end() != i);
770 }
771 #endif
772 if (from_pos != from_list.first)
773 { assert(nullptr != from_list.first->next); // at least 2 elements in the list
774 /* not the first element in from_list */ assert(from_pos.ptr == from_pos.ptr->prev->next);
775 from_pos.ptr->prev->next = from_pos.ptr->next;
776 if (nullptr != from_pos.ptr->next)
777 {
778 /* not the last element in from_list */ assert(from_pos.ptr == from_pos.ptr->next->prev);
779 from_pos.ptr->next->prev = from_pos.ptr->prev;
780 }
781 else
782 {
783 /* last element in from_list */ assert(from_pos.ptr == from_list.first->prev);
784 from_list.first->prev = from_pos.ptr->prev;
785 }
786 }
787 else
788 {
789 /* first element in from_list */ assert(nullptr == from_pos.ptr->prev->next);
790 from_list.first = from_pos.ptr->next;
791 if (!from_list.empty())
792 {
793 /* not the last element in from_list */ assert(from_pos.ptr == from_pos.ptr->next->prev);
794 from_pos.ptr->next->prev = from_pos.ptr->prev;
795 }
796 }
797 // update the pointers of from_pos and insert from_pos into this
798 // list
799 from_pos.ptr->next = to_pos.ptr;
800 if (end() == to_pos)
801 {
802 // from_pos becomes the last element in *this
803 if (empty())
804 {
805 from_pos.ptr->prev = from_pos.ptr;
806 first = from_pos.ptr; assert(check_linked_list()); assert(from_list.check_linked_list());
807 return;
808 }
809 from_pos.ptr->prev = before_end().ptr;
810 from_pos.ptr->prev->next = from_pos.ptr;
811 first->prev = from_pos.ptr;
812 }
813 else
814 {
815 /* from_pos does not become the last element in *this */ assert(!empty());
816 from_pos.ptr->prev = to_pos.ptr->prev;
817 to_pos.ptr->prev = from_pos.ptr;
818 if (to_pos.ptr == first)
819 {
820 // we insert a new element before the current list begin,
821 // so the begin should change.
822 first = from_pos.ptr;
823 }
824 else
825 {
826 from_pos.ptr->prev->next = from_pos.ptr;
827 }
828 } assert(check_linked_list()); assert(from_list.check_linked_list());
829 #ifndef NDEBUG
830 assert((end() == to_pos ? before_end().ptr
831 : iterator(to_pos.ptr->prev)) == from_pos.ptr);
832 for (const_iterator i=begin(); i != from_pos; ++i) { assert(end() != i); }
833 if (end() != to_pos) {
834 for (const_iterator i=begin(); i!=to_pos; ++i) { assert(end() != i); }
835 }
836 #endif
837 }
838
839
841 void erase(iterator const pos)
842 { assert(end() != pos); assert(nullptr != pos.ptr->prev); assert(!empty());
843 #ifndef NDEBUG
844 assert(check_linked_list());
845 for (const_iterator i = begin(); i != pos; ++i) { assert(end() != i); }
846 #endif
847 if (pos != first)
848 { assert(nullptr != first->next); // at least 2 elements in the list
849 /* not the first element */ assert(pos.ptr == pos.ptr->prev->next);
850 pos.ptr->prev->next = pos.ptr->next;
851 if (nullptr != pos.ptr->next)
852 {
853 /* not the last element */ assert(pos.ptr == pos.ptr->next->prev);
854 pos.ptr->next->prev = pos.ptr->prev;
855 }
856 else
857 {
858 /* last element */ assert(pos.ptr == first->prev);
859 first->prev = pos.ptr->prev;
860 }
861 }
862 else
863 {
864 /* first element */ assert(nullptr == pos.ptr->prev->next);
865 first = pos.ptr->next;
866 if (!empty())
867 {
868 /* not the last element */ assert(pos.ptr == pos.ptr->next->prev);
869 pos.ptr->next->prev = pos.ptr->prev;
870 }
871 }
872 #ifdef USE_POOL_ALLOCATOR
873 get_pool().destroy(pos.ptr);
874 #else
875 delete pos.ptr;
876 #endif
877 }
878
879
883 #ifdef NDEBUG
884 static // only in debug mode it accesses data of the list itself
885 #endif
887 #ifndef NDEBUG
888 const // static functions cannot be const
889 #endif
890 { assert(end() != pos);
891 #ifndef NDEBUG
892 for (const_iterator i = begin(); i != pos; ++i) { assert(end() != i); }
893 #endif
894 return iterator(pos.ptr->next);
895 }
896
897
901 #ifdef NDEBUG
902 static // only in debug mode it accesses data of the list itself
903 #endif
905 #ifndef NDEBUG
906 const // static functions cannot be const
907 #endif
908 { assert(end() != pos);
909 #ifndef NDEBUG
910 for (const_iterator i = begin(); i != pos; ++i) { assert(end() != i); }
911 #endif
912 return const_iterator(pos.ptr->next);
913 }
914
915
920 { assert(pos!=end());
921 #ifndef NDEBUG
922 for (const_iterator i = begin(); i != pos; ++i) { assert(end() != i); }
923 #endif
924 return begin() == pos ? end() : iterator(pos.ptr->prev);
925 }
926
927
932 { assert(end() != pos);
933 #ifndef NDEBUG
934 for (const_iterator i = begin(); i != pos; ++i) { assert(end() != i); }
935 #endif
936 return begin() == pos ? end() : const_iterator(pos.ptr->prev);
937 }
938
939
940 bool operator==(const simple_list& other) const
941 {
942 const_iterator it = cbegin();
943 const_iterator other_it = other.cbegin();
944 while (cend() != it)
945 {
946 if (cend() == other_it || *it != *other_it)
947 {
948 return false;
949 }
950 ++it;
951 ++other_it;
952 }
953 return end()==other_it;
954 }
955
956
957 bool operator!=(const simple_list& other) const
958 {
959 return !operator==(other);
960 }
961 };
962
963 template <class El>
965#else
966 #define simple_list std::list
967
968 template <class El>
970 {
971 public:
972 typedef std::list<El>::iterator iterator;
973 typedef std::list<El>::const_iterator const_iterator;
974 private:
975 const void* null;
976 iterator iter;
977 public:
980 {
981 if constexpr (!std::is_trivially_destructible<iterator>::value)
982 {
983 // We still have to internally decide whether to construct
984 // the iterator or not so the destructor knows what to do.
985 null = nullptr;
986 }
987 }
988
989
991 explicit iterator_or_null_t(nullptr_t)
992 {
993 null = nullptr;
994 }
995
996
998 explicit iterator_or_null_t(const iterator other)
999 {
1000 new (&iter) iterator(other); assert(nullptr != null);
1001 }
1002
1003
1005 bool is_null() const { return nullptr == null; }
1006
1007
1009 // not)
1011 {
1012 if constexpr (!std::is_trivially_destructible<iterator>::value)
1013 {
1014 if (!is_null()) iter.~iterator();
1015 }
1016 }
1017
1018 El* operator->()
1019 { assert(nullptr != null);
1020 return iter.operator->();
1021 }
1022 El& operator*()
1023 { assert(nullptr != null);
1024 return iter.operator*();
1025 }
1026
1027 void operator=(nullptr_t)
1028 {
1029 if constexpr (!std::is_trivially_destructible<iterator>::value)
1030 {
1031 if (!is_null()) iter.~iterator();
1032 }
1033 null = nullptr;
1034 }
1035
1036
1037 explicit operator iterator() const
1038 { assert(nullptr != null);
1039 return iter;
1040 }
1041
1042
1043 void operator=(const iterator& other)
1044 {
1045 if constexpr (!std::is_trivially_destructible<iterator>::value)
1046 {
1047 if (!is_null()) iter.~iterator();
1048 }
1049 new (&iter) iterator(other); assert(nullptr != null);
1050 }
1051
1055 bool operator==(const iterator_or_null_t other) const
1056 {
1057 if constexpr (sizeof(null) == sizeof(iter))
1058 {
1059 return &*iter == &*other.iter;
1060 }
1061 else
1062 {
1063 return (is_null() && other.is_null()) ||
1064 (!is_null() && !other.is_null() && &*iter == &*other.iter);
1065 }
1066 }
1067
1068
1070 bool operator!=(const iterator_or_null_t other) const
1071 {
1072 return !operator==(other);
1073 }
1074
1075
1079 bool operator==(const const_iterator other) const
1080 { // assert(nullptr != &*other); -- generates a warning
1081 return (sizeof(null) == sizeof(iter) || !is_null()) &&
1082 &*iter == &*other;
1083 }
1084
1085
1086 bool operator!=(const const_iterator other) const
1087 {
1088 return !operator==(other);
1089 }
1090
1091
1095 bool operator==(const El* const other) const
1096 { assert(nullptr != other);
1097 return !is_null() && &*iter == other;
1098 }
1099
1100
1101 bool operator!=(const El* const other) const
1102 {
1103 return !operator==(other);
1104 }
1105 };
1106#endif
1107
1108} // end namespace detail
1109} // end namespace lts
1110} // end namespace mcrl2
1111
1112#endif // ifndef SIMPLE_LIST_H
pool_block_t(pool_block_t *const new_next_block)
char data[BLOCKSIZE - sizeof(pool_block_t *)]
U * construct(Args &&... args)
allocate and construct a new element (of any type)
static void *& deref_void(void *addr)
U * construct_samesize(Args &&... args)
allocate and construct a new element of the same size as the free list
void * first_free_T
first freed element
U * construct_othersize(Args &&... args)
allocate and construct a new element of a size that doesn't fit the free list
pool_block_t * first_block
first chunk in list of chunks
void destroy(U *const old_el)
destroy and delete some element
void * begin_used_in_first_block
start of part in the first chunk that is already in use
constant iterator class for simple_list
const_iterator & operator=(const const_iterator &other)=default
bool operator!=(const const_iterator &other) const
bool operator==(const const_iterator &other) const
const_iterator(const const_iterator &other)=default
entry(entry *const new_next, entry *const new_prev, Args &&... args)
class that stores either an iterator or a null value
bool operator==(const T *const other) const
bool operator!=(const T *const other) const
iterator class for simple_list
iterator(const iterator &other)=default
std::forward_iterator_tag iterator_category
iterator & operator=(const iterator &other)=default
a simple implementation of lists
bool empty() const
return true iff the list is empty
const_iterator before_end() const
iterator before_end()
return an iterator to the last element of the list
iterator prev(iterator pos) const
const_iterator cbegin() const
return a constant iterator to the first element of the list
static iterator end()
return an iterator past the last element of the list
iterator emplace_after(iterator pos, Args &&... args)
construct a new list entry after pos
const_iterator prev(const_iterator pos) const
entry * first
pointer to the beginning of the list
const_iterator next(const_iterator pos) const
const_iterator begin() const
return a constant iterator to the first element of the list
iterator emplace_front(Args &&... args)
construct a new list entry at the beginning
void splice_to_after(iterator const to_pos, simple_list< T > &from_list, iterator const from_pos)
iterator next(iterator pos) const
bool operator==(const simple_list &other) const
void splice(iterator const to_pos, simple_list< T > &from_list, iterator const from_pos)
move a list entry from one position to another (possibly in a different list) The function moves the ...
static my_pool< entry > & get_pool()
T & back()
return a reference to the last element of the list
T & front()
return a reference to the first element of the list
iterator begin()
return an iterator to the first element of the list
void erase(iterator const pos)
erase an element from a list
bool operator!=(const simple_list &other) const
iterator emplace(iterator pos, Args &&... args)
construct a new list entry before pos
static const_iterator cend()
return a constant iterator past the last element of the list
iterator emplace_back(Args &&... args)
Puts a new element at the end.
a pool allocator class
bool operator!=(const cs_game_node &n0, const cs_game_node &n1)
typename simple_list< El >::iterator_or_null iterator_or_null_t
bool operator==(const cs_game_node &n0, const cs_game_node &n1)
std::string ptr(const transition t)
Sorts the transitions on labels. Action with the tau label are put first.
A class that takes a linear process specification and checks all tau-summands of that LPS for conflue...
Definition indexed_set.h:72
STL namespace.
#define USE_POOL_ALLOCATOR
Definition simple_list.h:66