comparison CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/include/kj/list.h @ 69:33d812a61356

planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author jpayne
date Tue, 18 Mar 2025 17:55:14 -0400
parents
children
comparison
equal deleted inserted replaced
67:0e9998148a16 69:33d812a61356
1 // Copyright (c) 2021 Cloudflare, Inc. and contributors
2 // Licensed under the MIT License:
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21
22 #pragma once
23
24 #include "common.h"
25
26 KJ_BEGIN_HEADER
27
28 namespace kj {
29
30 template <typename T>
31 class ListLink;
32
33 template <typename T, typename MaybeConstT, ListLink<T> T::*link>
34 class ListIterator;
35
36 namespace _ { // (private)
37
38 KJ_NORETURN(void throwDoubleAdd());
39 KJ_NORETURN(void throwRemovedNotPresent());
40 KJ_NORETURN(void throwRemovedWrongList());
41 KJ_NORETURN(void throwDestroyedWhileInList());
42
43 } // namespace _ (private)
44
45 template <typename T, ListLink<T> T::*link>
46 class List {
47 // A linked list that does no memory allocation.
48 //
49 // The list contains elements of type T that are allocated elsewhere. An existing object of type
50 // T can be added to the list and removed again without doing any heap allocation. This is
51 // achieved by requiring that T contains a field of type ListLink<T>. A pointer-to-member to
52 // this field is the second parameter to the `List` template.
53 //
54 // kj::List is ideally suited to situations where an object wants to be able to "add itself" to
55 // a list of objects waiting for a notification, with the ability to remove itself early if it
56 // wants to stop waiting. With traditional STL containers, these operations would require memory
57 // allocation.
58 //
59 // Example:
60 //
61 // struct Item {
62 // ListLink<Waiter> link;
63 // // ... other members ...
64 // };
65 //
66 // kj::List<Item, &Item::link> itemList;
67 //
68 // Item foo;
69 // itemList.add(foo);
70 // itemList.remove(foo);
71 //
72 // Note that you MUST manually remove an element from the list before destroying it. ListLinks
73 // do not automatically unlink themselves because this could lead to subtle thread-safety bugs
74 // if the List is guarded by a mutex, and that mutex is not currently locked. Normally, you should
75 // have T's destructor remove it from any lists. You can use `link.isLinked()` to check if the
76 // item is currently in a list.
77 //
78 // kj::List is a doubly-linked list in order to allow O(1) removal of any element given only a
79 // reference to the element. However, it only supports forward iteration.
80 //
81 // When iterating over a kj::List, you can safely remove current element which the iterator
82 // points to without breaking the iteration. However, removing any *other* element could
83 // invalidate the iterator.
84
85 public:
86 List() = default;
87 KJ_DISALLOW_COPY_AND_MOVE(List);
88
89 bool empty() const {
90 return head == nullptr;
91 }
92
93 size_t size() const {
94 return listSize;
95 }
96
97 void add(T& element) {
98 if ((element.*link).prev != nullptr) _::throwDoubleAdd();
99 *tail = element;
100 (element.*link).prev = tail;
101 tail = &((element.*link).next);
102 ++listSize;
103 }
104
105 void addFront(T& element) {
106 if ((element.*link).prev != nullptr) _::throwDoubleAdd();
107 (element.*link).next = head;
108 (element.*link).prev = &head;
109 KJ_IF_MAYBE(oldHead, head) {
110 (oldHead->*link).prev = &(element.*link).next;
111 } else {
112 tail = &(element.*link).next;
113 }
114 head = element;
115 ++listSize;
116 }
117
118 void remove(T& element) {
119 if ((element.*link).prev == nullptr) _::throwRemovedNotPresent();
120 *((element.*link).prev) = (element.*link).next;
121 KJ_IF_MAYBE(n, (element.*link).next) {
122 (n->*link).prev = (element.*link).prev;
123 } else {
124 if (tail != &((element.*link).next)) _::throwRemovedWrongList();
125 tail = (element.*link).prev;
126 }
127 (element.*link).next = nullptr;
128 (element.*link).prev = nullptr;
129 --listSize;
130 }
131
132 typedef ListIterator<T, T, link> Iterator;
133 typedef ListIterator<T, const T, link> ConstIterator;
134
135 Iterator begin() { return Iterator(head); }
136 Iterator end() { return Iterator(nullptr); }
137 ConstIterator begin() const { return ConstIterator(head); }
138 ConstIterator end() const { return ConstIterator(nullptr); }
139
140 T& front() { return *begin(); }
141 const T& front() const { return *begin(); }
142
143 private:
144 Maybe<T&> head;
145 Maybe<T&>* tail = &head;
146 size_t listSize = 0;
147 };
148
149 template <typename T>
150 class ListLink {
151 public:
152 ListLink(): next(nullptr), prev(nullptr) {}
153 ~ListLink() noexcept {
154 // Intentionally `noexcept` because we want to crash if a dangling pointer was left in a list.
155 if (prev != nullptr) _::throwDestroyedWhileInList();
156 }
157 KJ_DISALLOW_COPY_AND_MOVE(ListLink);
158
159 bool isLinked() const { return prev != nullptr; }
160
161 private:
162 Maybe<T&> next;
163 Maybe<T&>* prev;
164
165 template <typename U, ListLink<U> U::*link>
166 friend class List;
167 template <typename U, typename MaybeConstU, ListLink<U> U::*link>
168 friend class ListIterator;
169 };
170
171 template <typename T, typename MaybeConstT, ListLink<T> T::*link>
172 class ListIterator {
173 public:
174 ListIterator() = default;
175
176 MaybeConstT& operator*() {
177 KJ_IREQUIRE(current != nullptr, "tried to dereference end of list");
178 return *_::readMaybe(current);
179 }
180 const T& operator*() const {
181 KJ_IREQUIRE(current != nullptr, "tried to dereference end of list");
182 return *_::readMaybe(current);
183 }
184 MaybeConstT* operator->() {
185 KJ_IREQUIRE(current != nullptr, "tried to dereference end of list");
186 return _::readMaybe(current);
187 }
188 const T* operator->() const {
189 KJ_IREQUIRE(current != nullptr, "tried to dereference end of list");
190 return _::readMaybe(current);
191 }
192
193 inline ListIterator& operator++() {
194 current = next;
195 next = current.map([](MaybeConstT& obj) -> kj::Maybe<MaybeConstT&> { return (obj.*link).next; })
196 .orDefault(nullptr);
197 return *this;
198 }
199 inline ListIterator operator++(int) {
200 ListIterator result = *this;
201 ++*this;
202 return result;
203 }
204
205 inline bool operator==(const ListIterator& other) const {
206 return _::readMaybe(current) == _::readMaybe(other.current);
207 }
208 inline bool operator!=(const ListIterator& other) const {
209 return _::readMaybe(current) != _::readMaybe(other.current);
210 }
211
212 private:
213 Maybe<MaybeConstT&> current;
214
215 Maybe<MaybeConstT&> next;
216 // so that the current item can be removed from the list without invalidating the iterator
217
218 explicit ListIterator(Maybe<MaybeConstT&> start)
219 : current(start),
220 next(start.map([](MaybeConstT& obj) -> kj::Maybe<MaybeConstT&> { return (obj.*link).next; })
221 .orDefault(nullptr)) {}
222 friend class List<T, link>;
223 };
224
225 } // namespace kj
226
227 KJ_END_HEADER