/src/spirv-tools/source/util/ilist.h
Line | Count | Source |
1 | | // Copyright (c) 2017 Google Inc. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://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, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | #ifndef SOURCE_UTIL_ILIST_H_ |
16 | | #define SOURCE_UTIL_ILIST_H_ |
17 | | |
18 | | #include <cassert> |
19 | | #include <memory> |
20 | | #include <type_traits> |
21 | | #include <vector> |
22 | | |
23 | | #include "source/util/ilist_node.h" |
24 | | |
25 | | namespace spvtools { |
26 | | namespace utils { |
27 | | |
28 | | // An IntrusiveList is a generic implementation of a doubly-linked list. The |
29 | | // intended convention for using this container is: |
30 | | // |
31 | | // class Node : public IntrusiveNodeBase<Node> { |
32 | | // // Note that "Node", the class being defined is the template. |
33 | | // // Must have a default constructor accessible to List. |
34 | | // // Add whatever data is needed in the node |
35 | | // }; |
36 | | // |
37 | | // using List = IntrusiveList<Node>; |
38 | | // |
39 | | // You can also inherit from IntrusiveList instead of a typedef if you want to |
40 | | // add more functionality. |
41 | | // |
42 | | // The condition on the template for IntrusiveNodeBase is there to add some type |
43 | | // checking to the container. The compiler will still allow inserting elements |
44 | | // of type IntrusiveNodeBase<Node>, but that would be an error. This assumption |
45 | | // allows NextNode and PreviousNode to return pointers to Node, and casting will |
46 | | // not be required by the user. |
47 | | |
48 | | template <class NodeType> |
49 | | class IntrusiveList { |
50 | | public: |
51 | | static_assert( |
52 | | std::is_base_of<IntrusiveNodeBase<NodeType>, NodeType>::value, |
53 | | "The type from the node must be derived from IntrusiveNodeBase, with " |
54 | | "itself in the template."); |
55 | | |
56 | | // Creates an empty list. |
57 | | inline IntrusiveList(); |
58 | | |
59 | | // Moves the contents of the given list to the list being constructed. |
60 | | IntrusiveList(IntrusiveList&&); |
61 | | |
62 | | // Destroys the list. Note that the elements of the list will not be deleted, |
63 | | // but they will be removed from the list. |
64 | | virtual ~IntrusiveList(); |
65 | | |
66 | | // Moves all of the elements in the list on the RHS to the list on the LHS. |
67 | | IntrusiveList& operator=(IntrusiveList&&); |
68 | | |
69 | | // Basetype for iterators so an IntrusiveList can be traversed like STL |
70 | | // containers. |
71 | | template <class T> |
72 | | class iterator_template { |
73 | | public: |
74 | 417M | iterator_template(const iterator_template& i) : node_(i.node_) {}spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction>::iterator_template(spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction> const&) Line | Count | Source | 74 | 389M | iterator_template(const iterator_template& i) : node_(i.node_) {} |
spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction const>::iterator_template(spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction const> const&) Line | Count | Source | 74 | 27.1M | iterator_template(const iterator_template& i) : node_(i.node_) {} |
|
75 | | |
76 | 243M | iterator_template& operator++() { |
77 | 243M | node_ = node_->next_node_; |
78 | 243M | return *this; |
79 | 243M | } spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction>::operator++() Line | Count | Source | 76 | 229M | iterator_template& operator++() { | 77 | 229M | node_ = node_->next_node_; | 78 | 229M | return *this; | 79 | 229M | } |
spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction const>::operator++() Line | Count | Source | 76 | 13.9M | iterator_template& operator++() { | 77 | 13.9M | node_ = node_->next_node_; | 78 | 13.9M | return *this; | 79 | 13.9M | } |
|
80 | | |
81 | 184M | iterator_template& operator--() { |
82 | 184M | node_ = node_->previous_node_; |
83 | 184M | return *this; |
84 | 184M | } spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction>::operator--() Line | Count | Source | 81 | 86.0M | iterator_template& operator--() { | 82 | 86.0M | node_ = node_->previous_node_; | 83 | 86.0M | return *this; | 84 | 86.0M | } |
spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction const>::operator--() Line | Count | Source | 81 | 98.2M | iterator_template& operator--() { | 82 | 98.2M | node_ = node_->previous_node_; | 83 | 98.2M | return *this; | 84 | 98.2M | } |
|
85 | | |
86 | 45.8M | iterator_template& operator=(const iterator_template& i) { |
87 | 45.8M | node_ = i.node_; |
88 | 45.8M | return *this; |
89 | 45.8M | } |
90 | | |
91 | 179M | T& operator*() const { return *node_; }spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction>::operator*() const Line | Count | Source | 91 | 157M | T& operator*() const { return *node_; } |
spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction const>::operator*() const Line | Count | Source | 91 | 22.0M | T& operator*() const { return *node_; } |
|
92 | 402M | T* operator->() const { return node_; }spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction>::operator->() const Line | Count | Source | 92 | 335M | T* operator->() const { return node_; } |
spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction const>::operator->() const Line | Count | Source | 92 | 66.2M | T* operator->() const { return node_; } |
|
93 | | |
94 | | friend inline bool operator==(const iterator_template& lhs, |
95 | 378M | const iterator_template& rhs) { |
96 | 378M | return lhs.node_ == rhs.node_; |
97 | 378M | } spvtools::utils::operator==(spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction> const&, spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction> const&) Line | Count | Source | 95 | 306M | const iterator_template& rhs) { | 96 | 306M | return lhs.node_ == rhs.node_; | 97 | 306M | } |
spvtools::utils::operator==(spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction const> const&, spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction const> const&) Line | Count | Source | 95 | 72.3M | const iterator_template& rhs) { | 96 | 72.3M | return lhs.node_ == rhs.node_; | 97 | 72.3M | } |
|
98 | | friend inline bool operator!=(const iterator_template& lhs, |
99 | 375M | const iterator_template& rhs) { |
100 | 375M | return !(lhs == rhs); |
101 | 375M | } spvtools::utils::operator!=(spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction> const&, spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction> const&) Line | Count | Source | 99 | 303M | const iterator_template& rhs) { | 100 | 303M | return !(lhs == rhs); | 101 | 303M | } |
spvtools::utils::operator!=(spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction const> const&, spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction const> const&) Line | Count | Source | 99 | 72.3M | const iterator_template& rhs) { | 100 | 72.3M | return !(lhs == rhs); | 101 | 72.3M | } |
|
102 | | |
103 | | // Moves the nodes in |list| to the list that |this| points to. The |
104 | | // positions of the nodes will be immediately before the element pointed to |
105 | | // by the iterator. The return value will be an iterator pointing to the |
106 | | // first of the newly inserted elements. |
107 | 568k | iterator_template MoveBefore(IntrusiveList* list) { |
108 | 568k | if (list->empty()) return *this; |
109 | | |
110 | 568k | NodeType* first_node = list->sentinel_.next_node_; |
111 | 568k | NodeType* last_node = list->sentinel_.previous_node_; |
112 | | |
113 | 568k | this->node_->previous_node_->next_node_ = first_node; |
114 | 568k | first_node->previous_node_ = this->node_->previous_node_; |
115 | | |
116 | 568k | last_node->next_node_ = this->node_; |
117 | 568k | this->node_->previous_node_ = last_node; |
118 | | |
119 | 568k | list->sentinel_.next_node_ = &list->sentinel_; |
120 | 568k | list->sentinel_.previous_node_ = &list->sentinel_; |
121 | | |
122 | 568k | return iterator(first_node); |
123 | 568k | } |
124 | | |
125 | | // Define standard iterator types needs so this class can be |
126 | | // used with <algorithms>. |
127 | | using iterator_category = std::bidirectional_iterator_tag; |
128 | | using difference_type = std::ptrdiff_t; |
129 | | using value_type = T; |
130 | | using pointer = T*; |
131 | | using const_pointer = const T*; |
132 | | using reference = T&; |
133 | | using const_reference = const T&; |
134 | | using size_type = size_t; |
135 | | |
136 | | protected: |
137 | | iterator_template() = delete; |
138 | 432M | inline iterator_template(T* node) { node_ = node; }spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction>::iterator_template(spvtools::opt::Instruction*) Line | Count | Source | 138 | 314M | inline iterator_template(T* node) { node_ = node; } |
spvtools::utils::IntrusiveList<spvtools::opt::Instruction>::iterator_template<spvtools::opt::Instruction const>::iterator_template(spvtools::opt::Instruction const*) Line | Count | Source | 138 | 117M | inline iterator_template(T* node) { node_ = node; } |
|
139 | | T* node_; |
140 | | |
141 | | friend IntrusiveList; |
142 | | }; |
143 | | |
144 | | using iterator = iterator_template<NodeType>; |
145 | | using const_iterator = iterator_template<const NodeType>; |
146 | | |
147 | | // Various types of iterators for the start (begin) and one past the end (end) |
148 | | // of the list. |
149 | | // |
150 | | // Decrementing |end()| iterator will give and iterator pointing to the last |
151 | | // element in the list, if one exists. |
152 | | // |
153 | | // Incrementing |end()| iterator will give |begin()|. |
154 | | // |
155 | | // Decrementing |begin()| will give |end()|. |
156 | | // |
157 | | // TODO: Not marking these functions as noexcept because Visual Studio 2013 |
158 | | // does not support it. When we no longer care about that compiler, we should |
159 | | // mark these as noexcept. |
160 | | iterator begin(); |
161 | | iterator end(); |
162 | | const_iterator begin() const; |
163 | | const_iterator end() const; |
164 | | const_iterator cbegin() const; |
165 | | const_iterator cend() const; |
166 | | |
167 | | // Appends |node| to the end of the list. If |node| is already in a list, it |
168 | | // will be removed from that list first. |
169 | | void push_back(NodeType* node); |
170 | | |
171 | | // Returns true if the list is empty. |
172 | | bool empty() const; |
173 | | |
174 | | // Makes the current list empty. |
175 | | inline void clear(); |
176 | | |
177 | | // Returns references to the first or last element in the list. It is an |
178 | | // error to call these functions on an empty list. |
179 | | NodeType& front(); |
180 | | NodeType& back(); |
181 | | const NodeType& front() const; |
182 | | const NodeType& back() const; |
183 | | |
184 | | // Transfers [|first|, |last|) from |other| into the list at |where|. |
185 | | // |
186 | | // If |other| is |this|, no change is made. |
187 | | void Splice(iterator where, IntrusiveList<NodeType>* other, iterator first, |
188 | | iterator last); |
189 | | |
190 | | protected: |
191 | | // Doing a deep copy of the list does not make sense if the list does not own |
192 | | // the data. It is not clear who will own the newly created data. Making |
193 | | // copies illegal for that reason. |
194 | | IntrusiveList(const IntrusiveList&) = delete; |
195 | | IntrusiveList& operator=(const IntrusiveList&) = delete; |
196 | | |
197 | | // This function will assert if it finds the list containing |node| is not in |
198 | | // a valid state. |
199 | | static void Check(NodeType* node); |
200 | | |
201 | | // A special node used to represent both the start and end of the list, |
202 | | // without being part of the list. |
203 | | NodeType sentinel_; |
204 | | }; |
205 | | |
206 | | // Implementation of IntrusiveList |
207 | | |
208 | | template <class NodeType> |
209 | 2.64M | inline IntrusiveList<NodeType>::IntrusiveList() : sentinel_() { |
210 | 2.64M | sentinel_.next_node_ = &sentinel_; |
211 | 2.64M | sentinel_.previous_node_ = &sentinel_; |
212 | 2.64M | sentinel_.is_sentinel_ = true; |
213 | 2.64M | } |
214 | | |
215 | | template <class NodeType> |
216 | | IntrusiveList<NodeType>::IntrusiveList(IntrusiveList&& list) : sentinel_() { |
217 | | sentinel_.next_node_ = &sentinel_; |
218 | | sentinel_.previous_node_ = &sentinel_; |
219 | | sentinel_.is_sentinel_ = true; |
220 | | list.sentinel_.ReplaceWith(&sentinel_); |
221 | | } |
222 | | |
223 | | template <class NodeType> |
224 | 2.64M | IntrusiveList<NodeType>::~IntrusiveList() { |
225 | 2.64M | clear(); |
226 | 2.64M | } |
227 | | |
228 | | template <class NodeType> |
229 | | IntrusiveList<NodeType>& IntrusiveList<NodeType>::operator=( |
230 | 0 | IntrusiveList<NodeType>&& list) { |
231 | 0 | list.sentinel_.ReplaceWith(&sentinel_); |
232 | 0 | return *this; |
233 | 0 | } |
234 | | |
235 | | template <class NodeType> |
236 | | inline typename IntrusiveList<NodeType>::iterator |
237 | 80.1M | IntrusiveList<NodeType>::begin() { |
238 | 80.1M | return iterator(sentinel_.next_node_); |
239 | 80.1M | } |
240 | | |
241 | | template <class NodeType> |
242 | | inline typename IntrusiveList<NodeType>::iterator |
243 | 232M | IntrusiveList<NodeType>::end() { |
244 | 232M | return iterator(&sentinel_); |
245 | 232M | } |
246 | | |
247 | | template <class NodeType> |
248 | | inline typename IntrusiveList<NodeType>::const_iterator |
249 | 2.87M | IntrusiveList<NodeType>::begin() const { |
250 | 2.87M | return const_iterator(sentinel_.next_node_); |
251 | 2.87M | } |
252 | | |
253 | | template <class NodeType> |
254 | | inline typename IntrusiveList<NodeType>::const_iterator |
255 | 2.87M | IntrusiveList<NodeType>::end() const { |
256 | 2.87M | return const_iterator(&sentinel_); |
257 | 2.87M | } |
258 | | |
259 | | template <class NodeType> |
260 | | inline typename IntrusiveList<NodeType>::const_iterator |
261 | 55.4M | IntrusiveList<NodeType>::cbegin() const { |
262 | 55.4M | return const_iterator(sentinel_.next_node_); |
263 | 55.4M | } |
264 | | |
265 | | template <class NodeType> |
266 | | inline typename IntrusiveList<NodeType>::const_iterator |
267 | 56.5M | IntrusiveList<NodeType>::cend() const { |
268 | 56.5M | return const_iterator(&sentinel_); |
269 | 56.5M | } |
270 | | |
271 | | template <class NodeType> |
272 | 30.0M | void IntrusiveList<NodeType>::push_back(NodeType* node) { |
273 | 30.0M | node->InsertBefore(&sentinel_); |
274 | 30.0M | } |
275 | | |
276 | | template <class NodeType> |
277 | 168M | bool IntrusiveList<NodeType>::empty() const { |
278 | 168M | return sentinel_.NextNode() == nullptr; |
279 | 168M | } |
280 | | |
281 | | template <class NodeType> |
282 | 2.64M | void IntrusiveList<NodeType>::clear() { |
283 | 2.64M | while (!empty()) { |
284 | 0 | front().RemoveFromList(); |
285 | 0 | } |
286 | 2.64M | } |
287 | | |
288 | | template <class NodeType> |
289 | 77.5M | NodeType& IntrusiveList<NodeType>::front() { |
290 | 77.5M | NodeType* node = sentinel_.NextNode(); |
291 | 77.5M | assert(node != nullptr && "Can't get the front of an empty list."); |
292 | 77.5M | return *node; |
293 | 77.5M | } |
294 | | |
295 | | template <class NodeType> |
296 | 94.5k | NodeType& IntrusiveList<NodeType>::back() { |
297 | 94.5k | NodeType* node = sentinel_.PreviousNode(); |
298 | 94.5k | assert(node != nullptr && "Can't get the back of an empty list."); |
299 | 94.5k | return *node; |
300 | 94.5k | } |
301 | | |
302 | | template <class NodeType> |
303 | | const NodeType& IntrusiveList<NodeType>::front() const { |
304 | | NodeType* node = sentinel_.NextNode(); |
305 | | assert(node != nullptr && "Can't get the front of an empty list."); |
306 | | return *node; |
307 | | } |
308 | | |
309 | | template <class NodeType> |
310 | 28.7M | const NodeType& IntrusiveList<NodeType>::back() const { |
311 | 28.7M | NodeType* node = sentinel_.PreviousNode(); |
312 | 28.7M | assert(node != nullptr && "Can't get the back of an empty list."); |
313 | 28.7M | return *node; |
314 | 28.7M | } |
315 | | |
316 | | template <class NodeType> |
317 | | void IntrusiveList<NodeType>::Splice(iterator where, |
318 | | IntrusiveList<NodeType>* other, |
319 | 9.38k | iterator first, iterator last) { |
320 | 9.38k | if (first == last) return; |
321 | 9.38k | if (other == this) return; |
322 | | |
323 | 9.38k | NodeType* first_prev = first.node_->previous_node_; |
324 | 9.38k | NodeType* where_next = where.node_->next_node_; |
325 | | |
326 | | // Attach first. |
327 | 9.38k | where.node_->next_node_ = first.node_; |
328 | 9.38k | first.node_->previous_node_ = where.node_; |
329 | | |
330 | | // Attach last. |
331 | 9.38k | where_next->previous_node_ = last.node_->previous_node_; |
332 | 9.38k | last.node_->previous_node_->next_node_ = where_next; |
333 | | |
334 | | // Fixup other. |
335 | 9.38k | first_prev->next_node_ = last.node_; |
336 | 9.38k | last.node_->previous_node_ = first_prev; |
337 | 9.38k | } |
338 | | |
339 | | template <class NodeType> |
340 | | void IntrusiveList<NodeType>::Check(NodeType* start) { |
341 | | int sentinel_count = 0; |
342 | | NodeType* p = start; |
343 | | do { |
344 | | assert(p != nullptr); |
345 | | assert(p->next_node_->previous_node_ == p); |
346 | | assert(p->previous_node_->next_node_ == p); |
347 | | if (p->is_sentinel_) sentinel_count++; |
348 | | p = p->next_node_; |
349 | | } while (p != start); |
350 | | assert(sentinel_count == 1 && "List should have exactly 1 sentinel node."); |
351 | | (void)sentinel_count; |
352 | | |
353 | | p = start; |
354 | | do { |
355 | | assert(p != nullptr); |
356 | | assert(p->previous_node_->next_node_ == p); |
357 | | assert(p->next_node_->previous_node_ == p); |
358 | | if (p->is_sentinel_) sentinel_count++; |
359 | | p = p->previous_node_; |
360 | | } while (p != start); |
361 | | } |
362 | | |
363 | | } // namespace utils |
364 | | } // namespace spvtools |
365 | | |
366 | | #endif // SOURCE_UTIL_ILIST_H_ |