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 | // Destorys 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 | iterator_template(const iterator_template& i) : node_(i.node_) {} |
75 | |
76 | iterator_template& operator++() { |
77 | node_ = node_->next_node_; |
78 | return *this; |
79 | } |
80 | |
81 | iterator_template& operator--() { |
82 | node_ = node_->previous_node_; |
83 | return *this; |
84 | } |
85 | |
86 | iterator_template& operator=(const iterator_template& i) { |
87 | node_ = i.node_; |
88 | return *this; |
89 | } |
90 | |
91 | T& operator*() const { return *node_; } |
92 | T* operator->() const { return node_; } |
93 | |
94 | friend inline bool operator==(const iterator_template& lhs, |
95 | const iterator_template& rhs) { |
96 | return lhs.node_ == rhs.node_; |
97 | } |
98 | friend inline bool operator!=(const iterator_template& lhs, |
99 | const iterator_template& rhs) { |
100 | return !(lhs == rhs); |
101 | } |
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 | iterator_template MoveBefore(IntrusiveList* list) { |
108 | if (list->empty()) return *this; |
109 | |
110 | NodeType* first_node = list->sentinel_.next_node_; |
111 | NodeType* last_node = list->sentinel_.previous_node_; |
112 | |
113 | this->node_->previous_node_->next_node_ = first_node; |
114 | first_node->previous_node_ = this->node_->previous_node_; |
115 | |
116 | last_node->next_node_ = this->node_; |
117 | this->node_->previous_node_ = last_node; |
118 | |
119 | list->sentinel_.next_node_ = &list->sentinel_; |
120 | list->sentinel_.previous_node_ = &list->sentinel_; |
121 | |
122 | return iterator(first_node); |
123 | } |
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 | 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 | inline IntrusiveList<NodeType>::IntrusiveList() : sentinel_() { |
210 | sentinel_.next_node_ = &sentinel_; |
211 | sentinel_.previous_node_ = &sentinel_; |
212 | sentinel_.is_sentinel_ = true; |
213 | } |
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 | IntrusiveList<NodeType>::~IntrusiveList() { |
225 | clear(); |
226 | } |
227 | |
228 | template <class NodeType> |
229 | IntrusiveList<NodeType>& IntrusiveList<NodeType>::operator=( |
230 | IntrusiveList<NodeType>&& list) { |
231 | list.sentinel_.ReplaceWith(&sentinel_); |
232 | return *this; |
233 | } |
234 | |
235 | template <class NodeType> |
236 | inline typename IntrusiveList<NodeType>::iterator |
237 | IntrusiveList<NodeType>::begin() { |
238 | return iterator(sentinel_.next_node_); |
239 | } |
240 | |
241 | template <class NodeType> |
242 | inline typename IntrusiveList<NodeType>::iterator |
243 | IntrusiveList<NodeType>::end() { |
244 | return iterator(&sentinel_); |
245 | } |
246 | |
247 | template <class NodeType> |
248 | inline typename IntrusiveList<NodeType>::const_iterator |
249 | IntrusiveList<NodeType>::begin() const { |
250 | return const_iterator(sentinel_.next_node_); |
251 | } |
252 | |
253 | template <class NodeType> |
254 | inline typename IntrusiveList<NodeType>::const_iterator |
255 | IntrusiveList<NodeType>::end() const { |
256 | return const_iterator(&sentinel_); |
257 | } |
258 | |
259 | template <class NodeType> |
260 | inline typename IntrusiveList<NodeType>::const_iterator |
261 | IntrusiveList<NodeType>::cbegin() const { |
262 | return const_iterator(sentinel_.next_node_); |
263 | } |
264 | |
265 | template <class NodeType> |
266 | inline typename IntrusiveList<NodeType>::const_iterator |
267 | IntrusiveList<NodeType>::cend() const { |
268 | return const_iterator(&sentinel_); |
269 | } |
270 | |
271 | template <class NodeType> |
272 | void IntrusiveList<NodeType>::push_back(NodeType* node) { |
273 | node->InsertBefore(&sentinel_); |
274 | } |
275 | |
276 | template <class NodeType> |
277 | bool IntrusiveList<NodeType>::empty() const { |
278 | return sentinel_.NextNode() == nullptr; |
279 | } |
280 | |
281 | template <class NodeType> |
282 | void IntrusiveList<NodeType>::clear() { |
283 | while (!empty()) { |
284 | front().RemoveFromList(); |
285 | } |
286 | } |
287 | |
288 | template <class NodeType> |
289 | NodeType& IntrusiveList<NodeType>::front() { |
290 | NodeType* node = sentinel_.NextNode(); |
291 | assert(node != nullptr && "Can't get the front of an empty list." ); |
292 | return *node; |
293 | } |
294 | |
295 | template <class NodeType> |
296 | NodeType& IntrusiveList<NodeType>::back() { |
297 | NodeType* node = sentinel_.PreviousNode(); |
298 | assert(node != nullptr && "Can't get the back of an empty list." ); |
299 | return *node; |
300 | } |
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 | const NodeType& IntrusiveList<NodeType>::back() const { |
311 | NodeType* node = sentinel_.PreviousNode(); |
312 | assert(node != nullptr && "Can't get the back of an empty list." ); |
313 | return *node; |
314 | } |
315 | |
316 | template <class NodeType> |
317 | void IntrusiveList<NodeType>::Splice(iterator where, |
318 | IntrusiveList<NodeType>* other, |
319 | iterator first, iterator last) { |
320 | if (first == last) return; |
321 | if (other == this) return; |
322 | |
323 | NodeType* first_prev = first.node_->previous_node_; |
324 | NodeType* where_next = where.node_->next_node_; |
325 | |
326 | // Attach first. |
327 | where.node_->next_node_ = first.node_; |
328 | first.node_->previous_node_ = where.node_; |
329 | |
330 | // Attach last. |
331 | where_next->previous_node_ = last.node_->previous_node_; |
332 | last.node_->previous_node_->next_node_ = where_next; |
333 | |
334 | // Fixup other. |
335 | first_prev->next_node_ = last.node_; |
336 | last.node_->previous_node_ = first_prev; |
337 | } |
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 | |
352 | p = start; |
353 | do { |
354 | assert(p != nullptr); |
355 | assert(p->previous_node_->next_node_ == p); |
356 | assert(p->next_node_->previous_node_ == p); |
357 | if (p->is_sentinel_) sentinel_count++; |
358 | p = p->previous_node_; |
359 | } while (p != start); |
360 | } |
361 | |
362 | } // namespace utils |
363 | } // namespace spvtools |
364 | |
365 | #endif // SOURCE_UTIL_ILIST_H_ |
366 | |