Pigweed
 
Loading...
Searching...
No Matches
intrusive_list.h
1// Copyright 2024 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14#pragma once
15
16#include <cstddef>
17#include <limits>
18#include <type_traits>
19
20#include "pw_containers/internal/intrusive_item.h"
21#include "pw_containers/internal/intrusive_list_item.h"
22
23namespace pw::containers::internal {
24
32template <typename Item>
34 public:
35 static_assert(std::is_same_v<Item, IntrusiveForwardListItem> ||
36 std::is_same_v<Item, IntrusiveListItem>);
37
38 constexpr GenericIntrusiveList() : head_() {}
39
40 template <typename Iterator>
41 GenericIntrusiveList(Iterator first, Iterator last) : GenericIntrusiveList() {
42 insert_after(&head_, first, last);
43 }
44
45 // Intrusive lists cannot be copied, since each Item can only be in one list.
47 GenericIntrusiveList& operator=(const GenericIntrusiveList&) = delete;
48
49 // Moves the other list's contents into this list.
51 head_.replace(other.head_);
52 }
53
54 // Clears this list and moves the other list's contents into it.
55 GenericIntrusiveList& operator=(GenericIntrusiveList&& other) {
56 clear();
57 head_.replace(other.head_);
58 return *this;
59 }
60
61 ~GenericIntrusiveList() { CheckIntrusiveContainerIsEmpty(empty()); }
62
63 template <typename Iterator>
64 void assign(Iterator first, Iterator last) {
65 clear();
66 insert_after(&head_, first, last);
67 }
68
69 // Iterators
70
72 constexpr Item* before_begin() noexcept { return &head_; }
73 constexpr const Item* before_begin() const noexcept { return &head_; }
74
76 constexpr Item* begin() noexcept { return head_.next_; }
77 constexpr const Item* begin() const noexcept { return head_.next_; }
78
80 Item* before_end() noexcept { return before_begin()->previous(); }
81
83 constexpr Item* end() noexcept { return &head_; }
84 constexpr const Item* end() const noexcept { return &head_; }
85
86 // Capacity
87
88 bool empty() const noexcept { return begin() == end(); }
89
93 constexpr size_t max_size() const noexcept {
94 return std::numeric_limits<ptrdiff_t>::max();
95 }
96
97 // Modifiers
98
100 void clear() {
101 while (!empty()) {
103 }
104 }
105
115 static Item* insert_after(Item* prev, Item& item) {
116 CheckIntrusiveItemIsUncontained(item.unlisted());
117 item.next_ = prev->next_;
118 item.set_previous(prev);
119 prev->next_ = &item;
120 item.next_->set_previous(&item);
121 return &item;
122 }
123
129 template <typename Iterator>
130 static Item* insert_after(Item* prev, Iterator first, Iterator last) {
131 for (Iterator it = first; it != last; ++it) {
132 prev = insert_after(prev, *(FromIterator(it)));
133 }
134 return prev;
135 }
136
143 static Item* erase_after(Item* item) {
144 item->next_->unlist(item);
145 return item->next_;
146 }
147
149 static Item* erase_after(Item* first, Item* last) {
150 while (first->next_ != last) {
151 erase_after(first);
152 }
153 return last;
154 }
155
156 // Exchanges this list's items with the `other` list's items. O(1) for
157 // IntrusiveList, O(n) for IntrusiveForwardList.
158 void swap(GenericIntrusiveList<Item>& other) {
159 Item tmp;
160 tmp.replace(head_);
161 head_.replace(other.head_);
162 other.head_.replace(tmp);
163 }
164
165 // Operations
166
172 template <typename Compare>
173 void merge(GenericIntrusiveList<Item>& other, Compare comp) {
174 Item* prev = before_begin();
175 Item* item = begin();
176 Item* other_item = other.begin();
177 while (!other.empty()) {
178 if (item == end() || !comp(*item, *other_item)) {
179 Item* tmp = other_item;
180 other_item = other.erase_after(other.before_begin());
181 prev = insert_after(prev, *tmp);
182 } else {
183 prev = item;
184 item = item->next_;
185 }
186 }
187 }
188
191 static void splice_after(Item* pos,
193 Item* first,
194 Item* last) {
195 // Return if the range is empty, unless it is from before_begin to end.
196 if (first == last && first != &other.head_) {
197 return;
198 }
199 Item* first_next = first->next_;
200 if (first_next == last) {
201 return;
202 }
203 Item* last_prev = last->previous();
204 Item* pos_next = pos->next_;
205
206 first->next_ = last;
207 last->set_previous(first);
208
209 pos->next_ = first_next;
210 first_next->set_previous(pos);
211
212 pos_next->set_previous(last_prev);
213 last_prev->next_ = pos_next;
214 }
215
216 // Removes this specific item from the list, if it is present. Finds the item
217 // by identity (address comparison) rather than value equality. Returns true
218 // if the item was removed; false if it was not present.
219 bool remove(const Item& item_to_remove) {
220 return remove_if(
221 [&item_to_remove](const Item& item) -> bool {
222 return &item_to_remove == &item;
223 },
224 1) != 0;
225 }
226
233 template <typename UnaryPredicate>
234 size_t remove_if(UnaryPredicate pred,
235 size_t max = std::numeric_limits<size_t>::max()) {
236 size_t removed = 0;
237 Item* prev = before_begin();
238 while (true) {
239 Item* item = prev->next_;
240 if (item == end()) {
241 break;
242 }
243 if (pred(*item)) {
244 erase_after(prev);
245 ++removed;
246 if (removed == max) {
247 break;
248 }
249 } else {
250 prev = item;
251 }
252 }
253 return removed;
254 }
255
257 void reverse() {
259 while (!empty()) {
260 Item* item = begin();
262 list.insert_after(list.before_begin(), *item);
263 }
264 splice_after(before_begin(), list, list.before_begin(), list.end());
265 }
266
272 template <typename BinaryPredicate>
273 size_t unique(BinaryPredicate pred) {
274 if (empty()) {
275 return 0;
276 }
277 size_t removed = 0;
278 Item* prev = begin();
279 while (true) {
280 Item* item = prev->next_;
281 if (item == end()) {
282 break;
283 }
284 if (pred(*prev, *item)) {
285 erase_after(prev);
286 ++removed;
287 } else {
288 prev = item;
289 }
290 }
291 return removed;
292 }
293
299 template <typename Compare>
300 void sort(Compare comp) {
301 Item* mid = begin();
302 bool even = true;
303 for (Item* item = begin(); item != end(); item = item->next_) {
304 even = !even;
305 if (even) {
306 mid = mid->next_;
307 }
308 }
309
310 // Partition the list.
312 tmp.splice_after(tmp.before_begin(), *this, before_begin(), mid);
313
314 // Check if trivially sorted.
315 if (tmp.empty()) {
316 return;
317 }
318
319 // Sort the sublists.
320 tmp.sort(comp);
321 sort(comp);
322
323 // Merge the sublists.
324 merge(tmp, comp);
325 }
326
327 private:
328 template <typename Iterator>
329 static Item* FromIterator(Iterator& it) {
330 if constexpr (std::is_pointer<std::remove_reference_t<decltype(*it)>>()) {
331 return *it;
332 } else {
333 return &(*it);
334 }
335 }
336
337 // Use an Item for the head pointer. This gives simpler logic for inserting
338 // elements compared to using an Item*. It also makes it possible to use
339 // &head_ for end(), rather than nullptr. This makes end() unique for each
340 // List and ensures that items already in a list cannot be added to another.
341 Item head_;
342};
343
344} // namespace pw::containers::internal
Definition: intrusive_list.h:33
static Item * erase_after(Item *first, Item *last)
Removes the range of items exclusively between first and last.
Definition: intrusive_list.h:149
size_t unique(BinaryPredicate pred)
Definition: intrusive_list.h:273
Item * before_end() noexcept
Returns a pointer to the last item.
Definition: intrusive_list.h:80
void sort(Compare comp)
Definition: intrusive_list.h:300
constexpr size_t max_size() const noexcept
Definition: intrusive_list.h:93
size_t remove_if(UnaryPredicate pred, size_t max=std::numeric_limits< size_t >::max())
Definition: intrusive_list.h:234
static Item * insert_after(Item *prev, Iterator first, Iterator last)
Definition: intrusive_list.h:130
void reverse()
Reverses the order of items in the list.
Definition: intrusive_list.h:257
constexpr Item * before_begin() noexcept
Returns a pointer to the sentinel item.
Definition: intrusive_list.h:72
constexpr Item * begin() noexcept
Returns a pointer to the first item.
Definition: intrusive_list.h:76
void merge(GenericIntrusiveList< Item > &other, Compare comp)
Definition: intrusive_list.h:173
static Item * insert_after(Item *prev, Item &item)
Definition: intrusive_list.h:115
static void splice_after(Item *pos, GenericIntrusiveList< Item > &other, Item *first, Item *last)
Definition: intrusive_list.h:191
void clear()
Removes all items from the list.
Definition: intrusive_list.h:100
constexpr Item * end() noexcept
Returns a pointer to the sentinel item.
Definition: intrusive_list.h:83
static Item * erase_after(Item *item)
Definition: intrusive_list.h:143