/src/spirv-tools/source/util/ilist_node.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_NODE_H_ |
16 | | #define SOURCE_UTIL_ILIST_NODE_H_ |
17 | | |
18 | | #include <cassert> |
19 | | |
20 | | namespace spvtools { |
21 | | namespace utils { |
22 | | |
23 | | template <class NodeType> |
24 | | class IntrusiveList; |
25 | | |
26 | | // IntrusiveNodeBase is the base class for nodes in an IntrusiveList. |
27 | | // See the comments in ilist.h on how to use the class. |
28 | | |
29 | | template <class NodeType> |
30 | | class IntrusiveNodeBase { |
31 | | public: |
32 | | // Creates a new node that is not in a list. |
33 | | inline IntrusiveNodeBase(); |
34 | | inline IntrusiveNodeBase(const IntrusiveNodeBase&); |
35 | | inline IntrusiveNodeBase& operator=(const IntrusiveNodeBase&); |
36 | | inline IntrusiveNodeBase(IntrusiveNodeBase&& that); |
37 | | |
38 | | // Destroys a node. It is an error to destroy a node that is part of a |
39 | | // list, unless it is a sentinel. |
40 | | virtual ~IntrusiveNodeBase(); |
41 | | |
42 | | IntrusiveNodeBase& operator=(IntrusiveNodeBase&& that); |
43 | | |
44 | | // Returns true if |this| is in a list. |
45 | | inline bool IsInAList() const; |
46 | | |
47 | | // Returns the node that comes after the given node in the list, if one |
48 | | // exists. If the given node is not in a list or is at the end of the list, |
49 | | // the return value is nullptr. |
50 | | inline NodeType* NextNode() const; |
51 | | |
52 | | // Returns the node that comes before the given node in the list, if one |
53 | | // exists. If the given node is not in a list or is at the start of the |
54 | | // list, the return value is nullptr. |
55 | | inline NodeType* PreviousNode() const; |
56 | | |
57 | | // Inserts the given node immediately before |pos| in the list. |
58 | | // If the given node is already in a list, it will first be removed |
59 | | // from that list. |
60 | | // |
61 | | // It is assumed that the given node is of type NodeType. It is an error if |
62 | | // |pos| is not already in a list. |
63 | | inline void InsertBefore(NodeType* pos); |
64 | | |
65 | | // Inserts the given node immediately after |pos| in the list. |
66 | | // If the given node is already in a list, it will first be removed |
67 | | // from that list. |
68 | | // |
69 | | // It is assumed that the given node is of type NodeType. It is an error if |
70 | | // |pos| is not already in a list, or if |pos| is equal to |this|. |
71 | | inline void InsertAfter(NodeType* pos); |
72 | | |
73 | | // Removes the given node from the list. It is assumed that the node is |
74 | | // in a list. Note that this does not free any storage related to the node, |
75 | | // it becomes the caller's responsibility to free the storage. |
76 | | inline void RemoveFromList(); |
77 | | |
78 | | protected: |
79 | | // Replaces |this| with |target|. |this| is a sentinel if and only if |
80 | | // |target| is also a sentinel. |
81 | | // |
82 | | // If neither node is a sentinel, |target| takes |
83 | | // the place of |this|. It is assumed that |target| is not in a list. |
84 | | // |
85 | | // If both are sentinels, then it will cause all of the |
86 | | // nodes in the list containing |this| to be moved to the list containing |
87 | | // |target|. In this case, it is assumed that |target| is an empty list. |
88 | | // |
89 | | // No storage will be deleted. |
90 | | void ReplaceWith(NodeType* target); |
91 | | |
92 | | // Returns true if |this| is the sentinel node of an empty list. |
93 | | bool IsEmptyList(); |
94 | | |
95 | | // The pointers to the next and previous nodes in the list. |
96 | | // If the current node is not part of a list, then |next_node_| and |
97 | | // |previous_node_| are equal to |nullptr|. |
98 | | NodeType* next_node_; |
99 | | NodeType* previous_node_; |
100 | | |
101 | | // Only true for the sentinel node stored in the list itself. |
102 | | bool is_sentinel_; |
103 | | |
104 | | friend IntrusiveList<NodeType>; |
105 | | }; |
106 | | |
107 | | // Implementation of IntrusiveNodeBase |
108 | | |
109 | | template <class NodeType> |
110 | | inline IntrusiveNodeBase<NodeType>::IntrusiveNodeBase() |
111 | 41.1M | : next_node_(nullptr), previous_node_(nullptr), is_sentinel_(false) {} |
112 | | |
113 | | template <class NodeType> |
114 | | inline IntrusiveNodeBase<NodeType>::IntrusiveNodeBase( |
115 | 2.37M | const IntrusiveNodeBase&) { |
116 | 2.37M | next_node_ = nullptr; |
117 | 2.37M | previous_node_ = nullptr; |
118 | 2.37M | is_sentinel_ = false; |
119 | 2.37M | } |
120 | | |
121 | | template <class NodeType> |
122 | | inline IntrusiveNodeBase<NodeType>& IntrusiveNodeBase<NodeType>::operator=( |
123 | 0 | const IntrusiveNodeBase&) { |
124 | 0 | assert(!is_sentinel_); |
125 | 0 | if (IsInAList()) { |
126 | 0 | RemoveFromList(); |
127 | 0 | } |
128 | 0 | return *this; |
129 | 0 | } |
130 | | |
131 | | template <class NodeType> |
132 | | inline IntrusiveNodeBase<NodeType>::IntrusiveNodeBase(IntrusiveNodeBase&& that) |
133 | | : next_node_(nullptr), |
134 | | previous_node_(nullptr), |
135 | | is_sentinel_(that.is_sentinel_) { |
136 | | if (is_sentinel_) { |
137 | | next_node_ = this; |
138 | | previous_node_ = this; |
139 | | } |
140 | | that.ReplaceWith(this); |
141 | | } |
142 | | |
143 | | template <class NodeType> |
144 | 43.5M | IntrusiveNodeBase<NodeType>::~IntrusiveNodeBase() { |
145 | 43.5M | assert(is_sentinel_ || !IsInAList()); |
146 | 43.5M | } |
147 | | |
148 | | template <class NodeType> |
149 | | IntrusiveNodeBase<NodeType>& IntrusiveNodeBase<NodeType>::operator=( |
150 | | IntrusiveNodeBase&& that) { |
151 | | that.ReplaceWith(this); |
152 | | return *this; |
153 | | } |
154 | | |
155 | | template <class NodeType> |
156 | 160M | inline bool IntrusiveNodeBase<NodeType>::IsInAList() const { |
157 | 160M | return next_node_ != nullptr; |
158 | 160M | } |
159 | | |
160 | | template <class NodeType> |
161 | 576M | inline NodeType* IntrusiveNodeBase<NodeType>::NextNode() const { |
162 | 576M | if (!next_node_->is_sentinel_) return next_node_; |
163 | 78.7M | return nullptr; |
164 | 576M | } |
165 | | |
166 | | template <class NodeType> |
167 | 31.9M | inline NodeType* IntrusiveNodeBase<NodeType>::PreviousNode() const { |
168 | 31.9M | if (!previous_node_->is_sentinel_) return previous_node_; |
169 | 0 | return nullptr; |
170 | 31.9M | } |
171 | | |
172 | | template <class NodeType> |
173 | 33.9M | inline void IntrusiveNodeBase<NodeType>::InsertBefore(NodeType* pos) { |
174 | 33.9M | assert(!this->is_sentinel_ && "Sentinel nodes cannot be moved around."); |
175 | 33.9M | assert(pos->IsInAList() && "Pos should already be in a list."); |
176 | 33.9M | if (this->IsInAList()) this->RemoveFromList(); |
177 | | |
178 | 33.9M | this->next_node_ = pos; |
179 | 33.9M | this->previous_node_ = pos->previous_node_; |
180 | 33.9M | pos->previous_node_ = static_cast<NodeType*>(this); |
181 | 33.9M | this->previous_node_->next_node_ = static_cast<NodeType*>(this); |
182 | 33.9M | } |
183 | | |
184 | | template <class NodeType> |
185 | 0 | inline void IntrusiveNodeBase<NodeType>::InsertAfter(NodeType* pos) { |
186 | 0 | assert(!this->is_sentinel_ && "Sentinel nodes cannot be moved around."); |
187 | 0 | assert(pos->IsInAList() && "Pos should already be in a list."); |
188 | 0 | assert(this != pos && "Can't insert a node after itself."); |
189 | | |
190 | 0 | if (this->IsInAList()) { |
191 | 0 | this->RemoveFromList(); |
192 | 0 | } |
193 | |
|
194 | 0 | this->previous_node_ = pos; |
195 | 0 | this->next_node_ = pos->next_node_; |
196 | 0 | pos->next_node_ = static_cast<NodeType*>(this); |
197 | 0 | this->next_node_->previous_node_ = static_cast<NodeType*>(this); |
198 | 0 | } |
199 | | |
200 | | template <class NodeType> |
201 | 33.9M | inline void IntrusiveNodeBase<NodeType>::RemoveFromList() { |
202 | 33.9M | assert(!this->is_sentinel_ && "Sentinel nodes cannot be moved around."); |
203 | 33.9M | assert(this->IsInAList() && |
204 | 33.9M | "Cannot remove a node from a list if it is not in a list."); |
205 | | |
206 | 33.9M | this->next_node_->previous_node_ = this->previous_node_; |
207 | 33.9M | this->previous_node_->next_node_ = this->next_node_; |
208 | 33.9M | this->next_node_ = nullptr; |
209 | 33.9M | this->previous_node_ = nullptr; |
210 | 33.9M | } |
211 | | |
212 | | template <class NodeType> |
213 | 0 | void IntrusiveNodeBase<NodeType>::ReplaceWith(NodeType* target) { |
214 | 0 | if (this->is_sentinel_) { |
215 | 0 | assert(target->IsEmptyList() && |
216 | 0 | "If target is not an empty list, the nodes in that list would not " |
217 | 0 | "be linked to a sentinel."); |
218 | 0 | } else { |
219 | 0 | assert(IsInAList() && "The node being replaced must be in a list."); |
220 | 0 | assert(!target->is_sentinel_ && |
221 | 0 | "Cannot turn a sentinel node into one that is not."); |
222 | 0 | } |
223 | 0 |
|
224 | 0 | if (!this->IsEmptyList()) { |
225 | 0 | // Link target into the same position that |this| was in. |
226 | 0 | target->next_node_ = this->next_node_; |
227 | 0 | target->previous_node_ = this->previous_node_; |
228 | 0 | target->next_node_->previous_node_ = target; |
229 | 0 | target->previous_node_->next_node_ = target; |
230 | 0 |
|
231 | 0 | // Reset |this| to itself default value. |
232 | 0 | if (!this->is_sentinel_) { |
233 | 0 | // Reset |this| so that it is not in a list. |
234 | 0 | this->next_node_ = nullptr; |
235 | 0 | this->previous_node_ = nullptr; |
236 | 0 | } else { |
237 | 0 | // Set |this| so that it is the head of an empty list. |
238 | 0 | // We cannot treat sentinel nodes like others because it is invalid for |
239 | 0 | // a sentinel node to not be in a list. |
240 | 0 | this->next_node_ = static_cast<NodeType*>(this); |
241 | 0 | this->previous_node_ = static_cast<NodeType*>(this); |
242 | 0 | } |
243 | 0 | } else { |
244 | 0 | // If |this| points to itself, it must be a sentinel node with an empty |
245 | 0 | // list. Reset |this| so that it is the head of an empty list. We want |
246 | 0 | // |target| to be the same. The asserts above guarantee that. |
247 | 0 | } |
248 | 0 | } |
249 | | |
250 | | template <class NodeType> |
251 | 0 | bool IntrusiveNodeBase<NodeType>::IsEmptyList() { |
252 | 0 | if (next_node_ == this) { |
253 | 0 | assert(is_sentinel_ && |
254 | 0 | "None sentinel nodes should never point to themselves."); |
255 | 0 | assert(previous_node_ == this && |
256 | 0 | "Inconsistency with the previous and next nodes."); |
257 | 0 | return true; |
258 | 0 | } |
259 | 0 | return false; |
260 | 0 | } |
261 | | |
262 | | } // namespace utils |
263 | | } // namespace spvtools |
264 | | |
265 | | #endif // SOURCE_UTIL_ILIST_NODE_H_ |