C/C++ API Reference
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
26
29
37template <typename Item>
39 public:
40 static_assert(std::is_same_v<Item, IntrusiveForwardListItem> ||
41 std::is_same_v<Item, IntrusiveListItem>);
42
43 constexpr GenericIntrusiveList() : head_() {}
44
45 template <typename Iterator>
46 GenericIntrusiveList(Iterator first, Iterator last) : GenericIntrusiveList() {
47 insert_after(&head_, first, last);
48 }
49
50 // Intrusive lists cannot be copied, since each Item can only be in one list.
52 GenericIntrusiveList& operator=(const GenericIntrusiveList&) = delete;
53
54 // Moves the other list's contents into this list.
56 head_.replace(other.head_);
57 }
58
59 // Clears this list and moves the other list's contents into it.
60 GenericIntrusiveList& operator=(GenericIntrusiveList&& other) {
61 clear();
62 head_.replace(other.head_);
63 return *this;
64 }
65
66 ~GenericIntrusiveList() { CheckIntrusiveContainerIsEmpty(empty()); }
67
68 template <typename Iterator>
69 void assign(Iterator first, Iterator last) {
70 clear();
71 insert_after(&head_, first, last);
72 }
73
74 // Iterators
75
77 constexpr Item* before_begin() noexcept { return &head_; }
78 constexpr const Item* before_begin() const noexcept { return &head_; }
79
81 constexpr Item* begin() noexcept { return head_.next_; }
82 constexpr const Item* begin() const noexcept { return head_.next_; }
83
85 Item* before_end() noexcept { return before_begin()->previous(); }
86
88 constexpr Item* end() noexcept { return &head_; }
89 constexpr const Item* end() const noexcept { return &head_; }
90
91 // Capacity
92
93 bool empty() const noexcept { return begin() == end(); }
94
98 constexpr size_t max_size() const noexcept {
99 return std::numeric_limits<ptrdiff_t>::max();
100 }
101
102 // Modifiers
103
105 void clear() {
106 while (!empty()) {
108 }
109 }
110
112 void push_back(Item& item) { insert_after(before_end(), item); }
113
115 void pop_back() { erase_after(before_end()->previous()); }
116
118 void push_front(Item& item) { insert_after(before_begin(), item); }
119
122
132 static Item* insert_after(Item* prev, Item& item) {
133 CheckIntrusiveItemIsUncontained(item.unlisted());
134 item.next_ = prev->next_;
135 item.set_previous(prev);
136 prev->next_ = &item;
137 item.next_->set_previous(&item);
138 return &item;
139 }
140
146 template <typename Iterator>
147 static Item* insert_after(Item* prev, Iterator first, Iterator last) {
148 for (Iterator it = first; it != last; ++it) {
149 prev = insert_after(prev, *(FromIterator(it)));
150 }
151 return prev;
152 }
153
160 static Item* erase_after(Item* item) {
161 item->next_->unlist(item);
162 return item->next_;
163 }
164
166 static Item* erase_after(Item* first, Item* last) {
167 while (first->next_ != last) {
168 erase_after(first);
169 }
170 return last;
171 }
172
173 // Exchanges this list's items with the `other` list's items. O(1) for
174 // IntrusiveList, O(n) for IntrusiveForwardList.
175 void swap(GenericIntrusiveList<Item>& other) {
176 Item tmp;
177 tmp.replace(head_);
178 head_.replace(other.head_);
179 other.head_.replace(tmp);
180 }
181
182 // Operations
183
189 template <typename Compare>
190 void merge(GenericIntrusiveList<Item>& other, Compare comp) {
191 Item* prev = before_begin();
192 Item* item = begin();
193 Item* other_item = other.begin();
194 while (!other.empty()) {
195 if (item == end() || !comp(*item, *other_item)) {
196 Item* tmp = other_item;
197 other_item = other.erase_after(other.before_begin());
198 prev = insert_after(prev, *tmp);
199 } else {
200 prev = item;
201 item = item->next_;
202 }
203 }
204 }
205
208 static void splice_after(Item* pos,
210 Item* first,
211 Item* last) {
212 // Return if the range is empty, unless it is from before_begin to end.
213 if (first == last && first != &other.head_) {
214 return;
215 }
216 Item* first_next = first->next_;
217 if (first_next == last) {
218 return;
219 }
220 Item* last_prev = last->previous();
221 Item* pos_next = pos->next_;
222
223 first->next_ = last;
224 last->set_previous(first);
225
226 pos->next_ = first_next;
227 first_next->set_previous(pos);
228
229 pos_next->set_previous(last_prev);
230 last_prev->next_ = pos_next;
231 }
232
233 // Removes this specific item from the list, if it is present. Finds the item
234 // by identity (address comparison) rather than value equality. Returns true
235 // if the item was removed; false if it was not present.
236 bool remove(const Item& item_to_remove) {
237 return remove_if(
238 [&item_to_remove](const Item& item) -> bool {
239 return &item_to_remove == &item;
240 },
241 1) != 0;
242 }
243
250 template <typename UnaryPredicate>
251 size_t remove_if(UnaryPredicate pred,
252 size_t max = std::numeric_limits<size_t>::max()) {
253 size_t removed = 0;
254 Item* prev = before_begin();
255 while (true) {
256 Item* item = prev->next_;
257 if (item == end()) {
258 break;
259 }
260 if (pred(*item)) {
261 erase_after(prev);
262 ++removed;
263 if (removed == max) {
264 break;
265 }
266 } else {
267 prev = item;
268 }
269 }
270 return removed;
271 }
272
274 void reverse() {
276 while (!empty()) {
277 Item* item = begin();
279 list.insert_after(list.before_begin(), *item);
280 }
281 splice_after(before_begin(), list, list.before_begin(), list.end());
282 }
283
289 template <typename BinaryPredicate>
290 size_t unique(BinaryPredicate pred) {
291 if (empty()) {
292 return 0;
293 }
294 size_t removed = 0;
295 Item* prev = begin();
296 while (true) {
297 Item* item = prev->next_;
298 if (item == end()) {
299 break;
300 }
301 if (pred(*prev, *item)) {
302 erase_after(prev);
303 ++removed;
304 } else {
305 prev = item;
306 }
307 }
308 return removed;
309 }
310
316 template <typename Compare>
317 void sort(Compare comp) {
318 Item* mid = begin();
319 bool even = true;
320 for (Item* item = begin(); item != end(); item = item->next_) {
321 even = !even;
322 if (even) {
323 mid = mid->next_;
324 }
325 }
326
327 // Partition the list.
329 tmp.splice_after(tmp.before_begin(), *this, before_begin(), mid);
330
331 // Check if trivially sorted.
332 if (tmp.empty()) {
333 return;
334 }
335
336 // Sort the sublists.
337 tmp.sort(comp);
338 sort(comp);
339
340 // Merge the sublists.
341 merge(tmp, comp);
342 }
343
344 private:
345 template <typename Iterator>
346 static Item* FromIterator(Iterator& it) {
347 if constexpr (std::is_pointer<std::remove_reference_t<decltype(*it)>>()) {
348 return *it;
349 } else {
350 return &(*it);
351 }
352 }
353
354 // Use an Item for the head pointer. This gives simpler logic for inserting
355 // elements compared to using an Item*. It also makes it possible to use
356 // &head_ for end(), rather than nullptr. This makes end() unique for each
357 // List and ensures that items already in a list cannot be added to another.
358 Item head_;
359};
360
361} // namespace pw::containers::internal
Definition: intrusive_list.h:38
static Item * erase_after(Item *first, Item *last)
Removes the range of items exclusively between first and last.
Definition: intrusive_list.h:166
size_t unique(BinaryPredicate pred)
Definition: intrusive_list.h:290
void push_front(Item &item)
Inserts an element at the beginning of the list.
Definition: intrusive_list.h:118
Item * before_end() noexcept
Returns a pointer to the last item.
Definition: intrusive_list.h:85
void sort(Compare comp)
Definition: intrusive_list.h:317
constexpr size_t max_size() const noexcept
Definition: intrusive_list.h:98
size_t remove_if(UnaryPredicate pred, size_t max=std::numeric_limits< size_t >::max())
Definition: intrusive_list.h:251
static Item * insert_after(Item *prev, Iterator first, Iterator last)
Definition: intrusive_list.h:147
void reverse()
Reverses the order of items in the list.
Definition: intrusive_list.h:274
void pop_front()
Removes the first item in the list. The list must not be empty.
Definition: intrusive_list.h:121
constexpr Item * before_begin() noexcept
Returns a pointer to the sentinel item.
Definition: intrusive_list.h:77
constexpr Item * begin() noexcept
Returns a pointer to the first item.
Definition: intrusive_list.h:81
void merge(GenericIntrusiveList< Item > &other, Compare comp)
Definition: intrusive_list.h:190
void pop_back()
Removes the last item in the list. The list must not be empty.
Definition: intrusive_list.h:115
static Item * insert_after(Item *prev, Item &item)
Definition: intrusive_list.h:132
static void splice_after(Item *pos, GenericIntrusiveList< Item > &other, Item *first, Item *last)
Definition: intrusive_list.h:208
void clear()
Removes all items from the list.
Definition: intrusive_list.h:105
void push_back(Item &item)
Inserts an element at the end of the list.
Definition: intrusive_list.h:112
constexpr Item * end() noexcept
Returns a pointer to the sentinel item.
Definition: intrusive_list.h:88
static Item * erase_after(Item *item)
Definition: intrusive_list.h:160