| 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 | : next_node_(nullptr), previous_node_(nullptr), is_sentinel_(false) {} |
| 112 | |
| 113 | template <class NodeType> |
| 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> |
| 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 | 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> |
| 156 | inline bool IntrusiveNodeBase<NodeType>::IsInAList() const { |
| 157 | return next_node_ != nullptr; |
| 158 | } |
| 159 | |
| 160 | template <class NodeType> |
| 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> |
| 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> |
| 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> |
| 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> |
| 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> |
| 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> |
| 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 | |