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
120 static Item* insert_after(Item* prev, Item& item) {
121 CheckIntrusiveItemIsUncontained(item.unlisted());
122 item.next_ = prev->next_;
123 item.set_previous(prev);
124 prev->next_ = &item;
125 item.next_->set_previous(&item);
126 return &item;
127 }
128
134 template <typename Iterator>
135 static Item* insert_after(Item* prev, Iterator first, Iterator last) {
136 for (Iterator it = first; it != last; ++it) {
137 prev = insert_after(prev, *(FromIterator(it)));
138 }
139 return prev;
140 }
141
148 static Item* erase_after(Item* item) {
149 item->next_->unlist(item);
150 return item->next_;
151 }
152
154 static Item* erase_after(Item* first, Item* last) {
155 while (first->next_ != last) {
156 erase_after(first);
157 }
158 return last;
159 }
160
161 // Exchanges this list's items with the `other` list's items. O(1) for
162 // IntrusiveList, O(n) for IntrusiveForwardList.
163 void swap(GenericIntrusiveList<Item>& other) {
164 Item tmp;
165 tmp.replace(head_);
166 head_.replace(other.head_);
167 other.head_.replace(tmp);
168 }
169
170 // Operations
171
177 template <typename Compare>
178 void merge(GenericIntrusiveList<Item>& other, Compare comp) {
179 Item* prev = before_begin();
180 Item* item = begin();
181 Item* other_item = other.begin();
182 while (!other.empty()) {
183 if (item == end() || !comp(*item, *other_item)) {
184 Item* tmp = other_item;
185 other_item = other.erase_after(other.before_begin());
186 prev = insert_after(prev, *tmp);
187 } else {
188 prev = item;
189 item = item->next_;
190 }
191 }
192 }
193
196 static void splice_after(Item* pos,
198 Item* first,
199 Item* last) {
200 // Return if the range is empty, unless it is from before_begin to end.
201 if (first == last && first != &other.head_) {
202 return;
203 }
204 Item* first_next = first->next_;
205 if (first_next == last) {
206 return;
207 }
208 Item* last_prev = last->previous();
209 Item* pos_next = pos->next_;
210
211 first->next_ = last;
212 last->set_previous(first);
213
214 pos->next_ = first_next;
215 first_next->set_previous(pos);
216
217 pos_next->set_previous(last_prev);
218 last_prev->next_ = pos_next;
219 }
220
221 // Removes this specific item from the list, if it is present. Finds the item
222 // by identity (address comparison) rather than value equality. Returns true
223 // if the item was removed; false if it was not present.
224 bool remove(const Item& item_to_remove) {
225 return remove_if(
226 [&item_to_remove](const Item& item) -> bool {
227 return &item_to_remove == &item;
228 },
229 1) != 0;
230 }
231
238 template <typename UnaryPredicate>
239 size_t remove_if(UnaryPredicate pred,
240 size_t max = std::numeric_limits<size_t>::max()) {
241 size_t removed = 0;
242 Item* prev = before_begin();
243 while (true) {
244 Item* item = prev->next_;
245 if (item == end()) {
246 break;
247 }
248 if (pred(*item)) {
249 erase_after(prev);
250 ++removed;
251 if (removed == max) {
252 break;
253 }
254 } else {
255 prev = item;
256 }
257 }
258 return removed;
259 }
260
262 void reverse() {
264 while (!empty()) {
265 Item* item = begin();
267 list.insert_after(list.before_begin(), *item);
268 }
269 splice_after(before_begin(), list, list.before_begin(), list.end());
270 }
271
277 template <typename BinaryPredicate>
278 size_t unique(BinaryPredicate pred) {
279 if (empty()) {
280 return 0;
281 }
282 size_t removed = 0;
283 Item* prev = begin();
284 while (true) {
285 Item* item = prev->next_;
286 if (item == end()) {
287 break;
288 }
289 if (pred(*prev, *item)) {
290 erase_after(prev);
291 ++removed;
292 } else {
293 prev = item;
294 }
295 }
296 return removed;
297 }
298
304 template <typename Compare>
305 void sort(Compare comp) {
306 Item* mid = begin();
307 bool even = true;
308 for (Item* item = begin(); item != end(); item = item->next_) {
309 even = !even;
310 if (even) {
311 mid = mid->next_;
312 }
313 }
314
315 // Partition the list.
317 tmp.splice_after(tmp.before_begin(), *this, before_begin(), mid);
318
319 // Check if trivially sorted.
320 if (tmp.empty()) {
321 return;
322 }
323
324 // Sort the sublists.
325 tmp.sort(comp);
326 sort(comp);
327
328 // Merge the sublists.
329 merge(tmp, comp);
330 }
331
332 private:
333 template <typename Iterator>
334 static Item* FromIterator(Iterator& it) {
335 if constexpr (std::is_pointer<std::remove_reference_t<decltype(*it)>>()) {
336 return *it;
337 } else {
338 return &(*it);
339 }
340 }
341
342 // Use an Item for the head pointer. This gives simpler logic for inserting
343 // elements compared to using an Item*. It also makes it possible to use
344 // &head_ for end(), rather than nullptr. This makes end() unique for each
345 // List and ensures that items already in a list cannot be added to another.
346 Item head_;
347};
348
349} // 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:154
size_t unique(BinaryPredicate pred)
Definition: intrusive_list.h:278
Item * before_end() noexcept
Returns a pointer to the last item.
Definition: intrusive_list.h:85
void sort(Compare comp)
Definition: intrusive_list.h:305
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:239
static Item * insert_after(Item *prev, Iterator first, Iterator last)
Definition: intrusive_list.h:135
void reverse()
Reverses the order of items in the list.
Definition: intrusive_list.h:262
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:178
static Item * insert_after(Item *prev, Item &item)
Definition: intrusive_list.h:120
static void splice_after(Item *pos, GenericIntrusiveList< Item > &other, Item *first, Item *last)
Definition: intrusive_list.h:196
void clear()
Removes all items from the list.
Definition: intrusive_list.h:105
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:148