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>
IntrusiveNodeBase()110 inline IntrusiveNodeBase<NodeType>::IntrusiveNodeBase()
111     : next_node_(nullptr), previous_node_(nullptr), is_sentinel_(false) {}
112 
113 template <class NodeType>
IntrusiveNodeBase(const IntrusiveNodeBase &)114 inline IntrusiveNodeBase<NodeType>::IntrusiveNodeBase(
115     const IntrusiveNodeBase&) {
116   next_node_ = nullptr;
117   previous_node_ = nullptr;
118   is_sentinel_ = false;
119 }
120 
121 template <class NodeType>
122 inline IntrusiveNodeBase<NodeType>& IntrusiveNodeBase<NodeType>::operator=(
123     const IntrusiveNodeBase&) {
124   assert(!is_sentinel_);
125   if (IsInAList()) {
126     RemoveFromList();
127   }
128   return *this;
129 }
130 
131 template <class NodeType>
IntrusiveNodeBase(IntrusiveNodeBase && that)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>
~IntrusiveNodeBase()144 IntrusiveNodeBase<NodeType>::~IntrusiveNodeBase() {
145   assert(is_sentinel_ || !IsInAList());
146 }
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>
IsInAList()156 inline bool IntrusiveNodeBase<NodeType>::IsInAList() const {
157   return next_node_ != nullptr;
158 }
159 
160 template <class NodeType>
NextNode()161 inline NodeType* IntrusiveNodeBase<NodeType>::NextNode() const {
162   if (!next_node_->is_sentinel_) return next_node_;
163   return nullptr;
164 }
165 
166 template <class NodeType>
PreviousNode()167 inline NodeType* IntrusiveNodeBase<NodeType>::PreviousNode() const {
168   if (!previous_node_->is_sentinel_) return previous_node_;
169   return nullptr;
170 }
171 
172 template <class NodeType>
InsertBefore(NodeType * pos)173 inline void IntrusiveNodeBase<NodeType>::InsertBefore(NodeType* pos) {
174   assert(!this->is_sentinel_ && "Sentinel nodes cannot be moved around.");
175   assert(pos->IsInAList() && "Pos should already be in a list.");
176   if (this->IsInAList()) this->RemoveFromList();
177 
178   this->next_node_ = pos;
179   this->previous_node_ = pos->previous_node_;
180   pos->previous_node_ = static_cast<NodeType*>(this);
181   this->previous_node_->next_node_ = static_cast<NodeType*>(this);
182 }
183 
184 template <class NodeType>
InsertAfter(NodeType * pos)185 inline void IntrusiveNodeBase<NodeType>::InsertAfter(NodeType* pos) {
186   assert(!this->is_sentinel_ && "Sentinel nodes cannot be moved around.");
187   assert(pos->IsInAList() && "Pos should already be in a list.");
188   assert(this != pos && "Can't insert a node after itself.");
189 
190   if (this->IsInAList()) {
191     this->RemoveFromList();
192   }
193 
194   this->previous_node_ = pos;
195   this->next_node_ = pos->next_node_;
196   pos->next_node_ = static_cast<NodeType*>(this);
197   this->next_node_->previous_node_ = static_cast<NodeType*>(this);
198 }
199 
200 template <class NodeType>
RemoveFromList()201 inline void IntrusiveNodeBase<NodeType>::RemoveFromList() {
202   assert(!this->is_sentinel_ && "Sentinel nodes cannot be moved around.");
203   assert(this->IsInAList() &&
204          "Cannot remove a node from a list if it is not in a list.");
205 
206   this->next_node_->previous_node_ = this->previous_node_;
207   this->previous_node_->next_node_ = this->next_node_;
208   this->next_node_ = nullptr;
209   this->previous_node_ = nullptr;
210 }
211 
212 template <class NodeType>
ReplaceWith(NodeType * target)213 void IntrusiveNodeBase<NodeType>::ReplaceWith(NodeType* target) {
214   if (this->is_sentinel_) {
215     assert(target->IsEmptyList() &&
216            "If target is not an empty list, the nodes in that list would not "
217            "be linked to a sentinel.");
218   } else {
219     assert(IsInAList() && "The node being replaced must be in a list.");
220     assert(!target->is_sentinel_ &&
221            "Cannot turn a sentinel node into one that is not.");
222   }
223 
224   if (!this->IsEmptyList()) {
225     // Link target into the same position that |this| was in.
226     target->next_node_ = this->next_node_;
227     target->previous_node_ = this->previous_node_;
228     target->next_node_->previous_node_ = target;
229     target->previous_node_->next_node_ = target;
230 
231     // Reset |this| to itself default value.
232     if (!this->is_sentinel_) {
233       // Reset |this| so that it is not in a list.
234       this->next_node_ = nullptr;
235       this->previous_node_ = nullptr;
236     } else {
237       // Set |this| so that it is the head of an empty list.
238       // We cannot treat sentinel nodes like others because it is invalid for
239       // a sentinel node to not be in a list.
240       this->next_node_ = static_cast<NodeType*>(this);
241       this->previous_node_ = static_cast<NodeType*>(this);
242     }
243   } else {
244     // If |this| points to itself, it must be a sentinel node with an empty
245     // list.  Reset |this| so that it is the head of an empty list.  We want
246     // |target| to be the same.  The asserts above guarantee that.
247   }
248 }
249 
250 template <class NodeType>
IsEmptyList()251 bool IntrusiveNodeBase<NodeType>::IsEmptyList() {
252   if (next_node_ == this) {
253     assert(is_sentinel_ &&
254            "None sentinel nodes should never point to themselves.");
255     assert(previous_node_ == this &&
256            "Inconsistency with the previous and next nodes.");
257     return true;
258   }
259   return false;
260 }
261 
262 }  // namespace utils
263 }  // namespace spvtools
264 
265 #endif  // SOURCE_UTIL_ILIST_NODE_H_
266