Iros
 
Loading...
Searching...
No Matches
list.h
Go to the documentation of this file.
1#pragma once
2
9#include "di/meta/core.h"
10#include "di/types/void.h"
11#include "di/util/addressof.h"
12#include "di/util/exchange.h"
13#include "di/util/movable_box.h"
15
16namespace di::container {
17template<typename Self>
18struct IntrusiveListTag : IntrusiveTagBase<IntrusiveListNode<Self>> {};
19
20struct DefaultIntrusiveListTag : IntrusiveListTag<DefaultIntrusiveListTag> {};
21
22template<typename T, typename Tag, typename Self>
23class IntrusiveList {
24private:
25 using Node = IntrusiveListNode<Tag>;
26 using ConcreteNode = decltype(Tag::node_type(in_place_type<T>));
27 using ConcreteSelf = meta::Conditional<SameAs<Void, Self>, IntrusiveList, Self>;
28
29 constexpr static bool is_sized = Tag::is_sized(in_place_type<T>);
30
31 constexpr auto down_cast_self() -> decltype(auto) {
32 if constexpr (concepts::SameAs<Void, Self>) {
33 return *this;
34 } else {
35 return static_cast<Self&>(*this);
36 }
37 }
38
39 struct Iterator : IteratorBase<Iterator, BidirectionalIteratorTag, T, isize> {
40 private:
41 friend class IntrusiveList;
42
43 public:
44 Iterator() = default;
45
46 constexpr Iterator(Node* node) : m_node(node) {}
47 constexpr Iterator(Node& node) : m_node(util::addressof(node)) {}
48
49 constexpr auto operator*() const -> T& {
50 return Tag::down_cast(in_place_type<T>, static_cast<ConcreteNode&>(*m_node));
51 }
52 constexpr auto operator->() const -> T* { return util::addressof(**this); }
53
54 constexpr void advance_one() { m_node = m_node->next; }
55 constexpr void back_one() { m_node = m_node->prev; }
56
57 private:
58 constexpr friend auto operator==(Iterator const& a, Iterator const& b) -> bool { return a.m_node == b.m_node; }
59
60 constexpr auto node() const -> Node* { return m_node; }
61
62 Node* m_node { nullptr };
63 };
64
65 using ConstIterator = container::ConstIteratorImpl<Iterator>;
66
67public:
68 constexpr IntrusiveList() { reset_head(); }
69
70 IntrusiveList(IntrusiveList const&) = delete;
71
72 constexpr IntrusiveList(IntrusiveList&& other) : IntrusiveList() { *this = util::move(other); }
73
74 auto operator=(IntrusiveList const&) -> IntrusiveList& = delete;
75
76 constexpr auto operator=(IntrusiveList&& other) -> IntrusiveList& {
77 if (this != &other) {
78 clear();
79 m_head.value().next = other.m_head.value().next;
80 m_head.value().prev = other.m_head.value().prev;
81
82 // Update references to the new dummy head.
83 m_head.value().next->prev = util::addressof(m_head.value());
84 m_head.value().prev->next = util::addressof(m_head.value());
85
86 if constexpr (is_sized) {
87 m_size.value = other.size();
88 }
89
90 other.reset_head();
91 }
92 return *this;
93 }
94
95 constexpr ~IntrusiveList() { clear(); }
96
97 constexpr auto empty() const -> bool { return head() == util::addressof(m_head.value()); }
98 constexpr auto size() const -> usize
99 requires(is_sized)
100 {
101 return m_size.value;
102 }
103 constexpr auto max_size() const -> usize { return math::NumericLimits<usize>::max; }
104
105 constexpr auto begin() -> Iterator { return Iterator(head()); }
106 constexpr auto end() -> Iterator { return Iterator(util::addressof(m_head.value())); }
107
108 constexpr auto begin() const -> Iterator { return Iterator(head()); }
109 constexpr auto end() const -> Iterator { return Iterator(const_cast<Node*>(util::addressof(m_head.value()))); }
110
111 constexpr auto front() {
112 return lift_bool(!empty()) % [&] {
113 return util::ref(*begin());
114 };
115 }
116 constexpr auto front() const {
117 return lift_bool(!empty()) % [&] {
118 return util::cref(*begin());
119 };
120 }
121
122 constexpr auto back() {
123 return lift_bool(!empty()) % [&] {
124 return util::ref(*container::prev(end()));
125 };
126 }
127 constexpr auto back() const {
128 return lift_bool(!empty()) % [&] {
129 return util::cref(*container::prev(end()));
130 };
131 }
132
133 constexpr void push_back(Node& node) { insert(end(), node); }
134 constexpr void push_front(Node& node) { insert(begin(), node); }
135
136 constexpr auto pop_front() {
137 return lift_bool(!empty()) % [&] {
138 auto it = begin();
139 erase(it);
140 return util::ref(*it);
141 };
142 }
143
144 constexpr auto pop_back() {
145 return lift_bool(!empty()) % [&] {
146 auto it = --end();
147 erase(it);
148 return util::ref(*it);
149 };
150 }
151
152 constexpr void clear() { erase(begin(), end()); }
153
154 constexpr auto insert(ConstIterator position, Node& node_ref) -> Iterator {
155 auto* node = util::addressof(node_ref);
156 auto* next = position.base().node();
157 auto* prev = next->prev;
158
159 node->next = next;
160 node->prev = prev;
161
162 prev->next = node;
163 next->prev = node;
164
165 if constexpr (is_sized) {
166 ++m_size.value;
167 }
168
169 Tag::did_insert(down_cast_self(), static_cast<ConcreteNode&>(node_ref));
170
171 return Iterator(node);
172 }
173
174 constexpr auto erase(ConstIterator position) -> Iterator { return erase(position, container::next(position)); }
175 constexpr auto erase(ConstIterator first, ConstIterator last) -> Iterator {
176 if (first == last) {
177 return last.base();
178 }
179
180 auto* prev = first.base().node()->prev;
181 auto* end = last.base().node();
182 prev->next = end;
183 end->prev = prev;
184
185 for (auto it = first; it != last;) {
186 auto save = it++;
187 Tag::did_remove(down_cast_self(), static_cast<ConcreteNode&>(*save.base().node()));
188
189 if constexpr (is_sized) {
190 --m_size.value;
191 }
192 }
193
194 return last.base();
195 }
196
197 constexpr void splice(ConstIterator position, IntrusiveList& other) { splice(position, util::move(other)); }
198 constexpr void splice(ConstIterator position, IntrusiveList&& other) {
199 splice(position, other, other.begin(), other.end());
200 }
201
202 constexpr void splice(ConstIterator position, IntrusiveList& other, ConstIterator it) {
203 splice(position, util::move(other), it);
204 }
205 constexpr void splice(ConstIterator position, IntrusiveList&& other, ConstIterator it) {
206 splice(position, other, it, container::next(it, 1));
207 }
208
209 constexpr void splice(ConstIterator position, IntrusiveList& other, ConstIterator first, ConstIterator last) {
210 splice(position, util::move(other), first, last);
211 }
212 constexpr void splice(ConstIterator position, IntrusiveList&& other, ConstIterator first, ConstIterator last) {
213 auto* first_node = first.base().node();
214 auto* last_node = last.base().node();
215 if (first_node == last_node) {
216 return;
217 }
218
219 // Unlink from other.
220 auto* inclusive_last_node = last_node->prev;
221 {
222 auto* prev_node = first_node->prev;
223 prev_node->next = last_node;
224 last_node->prev = prev_node;
225 }
226
227 // Relink with this.
228 {
229 auto* position_node = position.base().node();
230 auto* prev_node = position_node->prev;
231
232 prev_node->next = first_node;
233 first_node->prev = prev_node;
234
235 position_node->prev = inclusive_last_node;
236 inclusive_last_node->next = position_node;
237 }
238
239 // Adjust size.
240 if constexpr (is_sized) {
241 if (this != &other) {
242 auto size = 1 + container::distance(first, ConstIterator(Iterator(inclusive_last_node)));
243 this->m_size.value += size;
244 other.m_size.value -= size;
245 }
246 }
247 }
248
249private:
250 constexpr friend auto operator==(IntrusiveList const& a, IntrusiveList const& b) -> bool
252 {
253 return container::equal(a, b);
254 }
255
256 constexpr friend auto operator<=>(IntrusiveList const& a, IntrusiveList const& b)
258 {
259 return container::compare(a, b);
260 }
261
262 template<typename F>
264 constexpr friend auto tag_invoke(di::Tag<erase_if>, ConcreteSelf& self, F&& function) -> usize {
265 auto it = self.begin();
266 auto result = 0ZU;
267 while (it != self.end()) {
268 if (function(*it)) {
269 it = self.erase(it);
270 ++result;
271 } else {
272 ++it;
273 }
274 }
275 return result;
276 }
277
278 constexpr auto head() const -> Node* { return m_head.value().next; }
279 constexpr void set_head(Node* head) { m_head.value().next = head; }
280
281 constexpr void reset_head() {
282 m_head.value().next = m_head.value().prev = util::addressof(m_head.value());
283 if constexpr (is_sized) {
284 m_size.value = 0;
285 }
286 }
287
288 util::MovableBox<Node> m_head;
289 [[no_unique_address]] util::StoreIf<usize, is_sized> m_size { 0 };
290};
291}
292
293namespace di {
296}
constexpr auto base() const &-> Iter const &
Definition const_iterator_impl.h:37
Definition list_node.h:9
Definition list_forward_declaration.h:12
constexpr auto end() const -> Iterator
Definition list.h:109
constexpr auto erase(ConstIterator first, ConstIterator last) -> Iterator
Definition list.h:175
constexpr auto back()
Definition list.h:122
constexpr auto front()
Definition list.h:111
constexpr auto end() -> Iterator
Definition list.h:106
constexpr auto begin() const -> Iterator
Definition list.h:108
constexpr auto front() const
Definition list.h:116
constexpr auto pop_front()
Definition list.h:136
constexpr auto insert(ConstIterator position, Node &node_ref) -> Iterator
Definition list.h:154
constexpr void push_front(Node &node)
Definition list.h:134
constexpr friend auto operator==(IntrusiveList const &a, IntrusiveList const &b) -> bool requires(concepts::EqualityComparable< T >)
Definition list.h:250
constexpr auto operator=(IntrusiveList &&other) -> IntrusiveList &
Definition list.h:76
constexpr auto max_size() const -> usize
Definition list.h:103
constexpr auto erase(ConstIterator position) -> Iterator
Definition list.h:174
constexpr auto empty() const -> bool
Definition list.h:97
constexpr auto size() const -> usize requires(is_sized)
Definition list.h:98
constexpr auto begin() -> Iterator
Definition list.h:105
constexpr void splice(ConstIterator position, IntrusiveList &&other, ConstIterator first, ConstIterator last)
Definition list.h:212
constexpr void splice(ConstIterator position, IntrusiveList &other)
Definition list.h:197
constexpr void splice(ConstIterator position, IntrusiveList &other, ConstIterator first, ConstIterator last)
Definition list.h:209
constexpr void clear()
Definition list.h:152
IntrusiveList(IntrusiveList const &)=delete
constexpr auto pop_back()
Definition list.h:144
constexpr IntrusiveList()
Definition list.h:68
constexpr void splice(ConstIterator position, IntrusiveList &other, ConstIterator it)
Definition list.h:202
auto operator=(IntrusiveList const &) -> IntrusiveList &=delete
constexpr void splice(ConstIterator position, IntrusiveList &&other, ConstIterator it)
Definition list.h:205
constexpr void push_back(Node &node)
Definition list.h:133
constexpr friend auto operator<=>(IntrusiveList const &a, IntrusiveList const &b)
Definition list.h:256
constexpr auto back() const
Definition list.h:127
constexpr void splice(ConstIterator position, IntrusiveList &&other)
Definition list.h:198
constexpr IntrusiveList(IntrusiveList &&other)
Definition list.h:72
constexpr friend auto tag_invoke(di::Tag< erase_if >, ConcreteSelf &self, F &&function) -> usize
Definition list.h:264
constexpr ~IntrusiveList()
Definition list.h:95
Definition compare.h:82
Definition relation.h:7
Definition core.h:114
Definition compare.h:91
Definition sequence.h:12
constexpr auto prev
Definition prev.h:28
constexpr auto next
Definition next.h:35
constexpr auto distance
Definition distance.h:44
constexpr auto equal
Definition equal.h:46
constexpr auto erase
Definition erase.h:76
constexpr auto compare
Definition compare.h:40
Definition as_bool.h:8
detail::ConditionalHelper< value, T, U >::Type Conditional
Definition core.h:88
size_t usize
Definition integers.h:33
di::meta::Decay< decltype(T)> Tag
Definition tag_invoke.h:28
constexpr auto ref
Definition reference_wrapper.h:98
constexpr auto cref
Definition reference_wrapper.h:99
Definition zstring_parser.h:9
constexpr auto in_place_type
Definition in_place_type.h:12
constexpr auto lift_bool
Definition lift_bool.h:13
Definition intrusive_tag_base.h:8
Definition numeric_limits.h:7
T value
Definition store_if.h:8