| // Copyright (c) 2017 Google Inc. | 
 | // | 
 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | // you may not use this file except in compliance with the License. | 
 | // You may obtain a copy of the License at | 
 | // | 
 | //     http://www.apache.org/licenses/LICENSE-2.0 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | // See the License for the specific language governing permissions and | 
 | // limitations under the License. | 
 |  | 
 | #ifndef SOURCE_UTIL_ILIST_NODE_H_ | 
 | #define SOURCE_UTIL_ILIST_NODE_H_ | 
 |  | 
 | #include <cassert> | 
 |  | 
 | namespace spvtools { | 
 | namespace utils { | 
 |  | 
 | template <class NodeType> | 
 | class IntrusiveList; | 
 |  | 
 | // IntrusiveNodeBase is the base class for nodes in an IntrusiveList. | 
 | // See the comments in ilist.h on how to use the class. | 
 |  | 
 | template <class NodeType> | 
 | class IntrusiveNodeBase { | 
 |  public: | 
 |   // Creates a new node that is not in a list. | 
 |   inline IntrusiveNodeBase(); | 
 |   inline IntrusiveNodeBase(const IntrusiveNodeBase&); | 
 |   inline IntrusiveNodeBase& operator=(const IntrusiveNodeBase&); | 
 |   inline IntrusiveNodeBase(IntrusiveNodeBase&& that); | 
 |  | 
 |   // Destroys a node.  It is an error to destroy a node that is part of a | 
 |   // list, unless it is a sentinel. | 
 |   virtual ~IntrusiveNodeBase(); | 
 |  | 
 |   IntrusiveNodeBase& operator=(IntrusiveNodeBase&& that); | 
 |  | 
 |   // Returns true if |this| is in a list. | 
 |   inline bool IsInAList() const; | 
 |  | 
 |   // Returns the node that comes after the given node in the list, if one | 
 |   // exists.  If the given node is not in a list or is at the end of the list, | 
 |   // the return value is nullptr. | 
 |   inline NodeType* NextNode() const; | 
 |  | 
 |   // Returns the node that comes before the given node in the list, if one | 
 |   // exists.  If the given node is not in a list or is at the start of the | 
 |   // list, the return value is nullptr. | 
 |   inline NodeType* PreviousNode() const; | 
 |  | 
 |   // Inserts the given node immediately before |pos| in the list. | 
 |   // If the given node is already in a list, it will first be removed | 
 |   // from that list. | 
 |   // | 
 |   // It is assumed that the given node is of type NodeType.  It is an error if | 
 |   // |pos| is not already in a list. | 
 |   inline void InsertBefore(NodeType* pos); | 
 |  | 
 |   // Inserts the given node immediately after |pos| in the list. | 
 |   // If the given node is already in a list, it will first be removed | 
 |   // from that list. | 
 |   // | 
 |   // It is assumed that the given node is of type NodeType.  It is an error if | 
 |   // |pos| is not already in a list, or if |pos| is equal to |this|. | 
 |   inline void InsertAfter(NodeType* pos); | 
 |  | 
 |   // Removes the given node from the list.  It is assumed that the node is | 
 |   // in a list.  Note that this does not free any storage related to the node, | 
 |   // it becomes the caller's responsibility to free the storage. | 
 |   inline void RemoveFromList(); | 
 |  | 
 |  protected: | 
 |   // Replaces |this| with |target|.  |this| is a sentinel if and only if | 
 |   // |target| is also a sentinel. | 
 |   // | 
 |   // If neither node is a sentinel, |target| takes | 
 |   // the place of |this|.  It is assumed that |target| is not in a list. | 
 |   // | 
 |   // If both are sentinels, then it will cause all of the | 
 |   // nodes in the list containing |this| to be moved to the list containing | 
 |   // |target|.  In this case, it is assumed that |target| is an empty list. | 
 |   // | 
 |   // No storage will be deleted. | 
 |   void ReplaceWith(NodeType* target); | 
 |  | 
 |   // Returns true if |this| is the sentinel node of an empty list. | 
 |   bool IsEmptyList(); | 
 |  | 
 |   // The pointers to the next and previous nodes in the list. | 
 |   // If the current node is not part of a list, then |next_node_| and | 
 |   // |previous_node_| are equal to |nullptr|. | 
 |   NodeType* next_node_; | 
 |   NodeType* previous_node_; | 
 |  | 
 |   // Only true for the sentinel node stored in the list itself. | 
 |   bool is_sentinel_; | 
 |  | 
 |   friend IntrusiveList<NodeType>; | 
 | }; | 
 |  | 
 | // Implementation of IntrusiveNodeBase | 
 |  | 
 | template <class NodeType> | 
 | inline IntrusiveNodeBase<NodeType>::IntrusiveNodeBase() | 
 |     : next_node_(nullptr), previous_node_(nullptr), is_sentinel_(false) {} | 
 |  | 
 | template <class NodeType> | 
 | inline IntrusiveNodeBase<NodeType>::IntrusiveNodeBase( | 
 |     const IntrusiveNodeBase&) { | 
 |   next_node_ = nullptr; | 
 |   previous_node_ = nullptr; | 
 |   is_sentinel_ = false; | 
 | } | 
 |  | 
 | template <class NodeType> | 
 | inline IntrusiveNodeBase<NodeType>& IntrusiveNodeBase<NodeType>::operator=( | 
 |     const IntrusiveNodeBase&) { | 
 |   assert(!is_sentinel_); | 
 |   if (IsInAList()) { | 
 |     RemoveFromList(); | 
 |   } | 
 |   return *this; | 
 | } | 
 |  | 
 | template <class NodeType> | 
 | inline IntrusiveNodeBase<NodeType>::IntrusiveNodeBase(IntrusiveNodeBase&& that) | 
 |     : next_node_(nullptr), | 
 |       previous_node_(nullptr), | 
 |       is_sentinel_(that.is_sentinel_) { | 
 |   if (is_sentinel_) { | 
 |     next_node_ = this; | 
 |     previous_node_ = this; | 
 |   } | 
 |   that.ReplaceWith(this); | 
 | } | 
 |  | 
 | template <class NodeType> | 
 | IntrusiveNodeBase<NodeType>::~IntrusiveNodeBase() { | 
 |   assert(is_sentinel_ || !IsInAList()); | 
 | } | 
 |  | 
 | template <class NodeType> | 
 | IntrusiveNodeBase<NodeType>& IntrusiveNodeBase<NodeType>::operator=( | 
 |     IntrusiveNodeBase&& that) { | 
 |   that.ReplaceWith(this); | 
 |   return *this; | 
 | } | 
 |  | 
 | template <class NodeType> | 
 | inline bool IntrusiveNodeBase<NodeType>::IsInAList() const { | 
 |   return next_node_ != nullptr; | 
 | } | 
 |  | 
 | template <class NodeType> | 
 | inline NodeType* IntrusiveNodeBase<NodeType>::NextNode() const { | 
 |   if (!next_node_->is_sentinel_) return next_node_; | 
 |   return nullptr; | 
 | } | 
 |  | 
 | template <class NodeType> | 
 | inline NodeType* IntrusiveNodeBase<NodeType>::PreviousNode() const { | 
 |   if (!previous_node_->is_sentinel_) return previous_node_; | 
 |   return nullptr; | 
 | } | 
 |  | 
 | template <class NodeType> | 
 | inline void IntrusiveNodeBase<NodeType>::InsertBefore(NodeType* pos) { | 
 |   assert(!this->is_sentinel_ && "Sentinel nodes cannot be moved around."); | 
 |   assert(pos->IsInAList() && "Pos should already be in a list."); | 
 |   if (this->IsInAList()) this->RemoveFromList(); | 
 |  | 
 |   this->next_node_ = pos; | 
 |   this->previous_node_ = pos->previous_node_; | 
 |   pos->previous_node_ = static_cast<NodeType*>(this); | 
 |   this->previous_node_->next_node_ = static_cast<NodeType*>(this); | 
 | } | 
 |  | 
 | template <class NodeType> | 
 | inline void IntrusiveNodeBase<NodeType>::InsertAfter(NodeType* pos) { | 
 |   assert(!this->is_sentinel_ && "Sentinel nodes cannot be moved around."); | 
 |   assert(pos->IsInAList() && "Pos should already be in a list."); | 
 |   assert(this != pos && "Can't insert a node after itself."); | 
 |  | 
 |   if (this->IsInAList()) { | 
 |     this->RemoveFromList(); | 
 |   } | 
 |  | 
 |   this->previous_node_ = pos; | 
 |   this->next_node_ = pos->next_node_; | 
 |   pos->next_node_ = static_cast<NodeType*>(this); | 
 |   this->next_node_->previous_node_ = static_cast<NodeType*>(this); | 
 | } | 
 |  | 
 | template <class NodeType> | 
 | inline void IntrusiveNodeBase<NodeType>::RemoveFromList() { | 
 |   assert(!this->is_sentinel_ && "Sentinel nodes cannot be moved around."); | 
 |   assert(this->IsInAList() && | 
 |          "Cannot remove a node from a list if it is not in a list."); | 
 |  | 
 |   this->next_node_->previous_node_ = this->previous_node_; | 
 |   this->previous_node_->next_node_ = this->next_node_; | 
 |   this->next_node_ = nullptr; | 
 |   this->previous_node_ = nullptr; | 
 | } | 
 |  | 
 | template <class NodeType> | 
 | void IntrusiveNodeBase<NodeType>::ReplaceWith(NodeType* target) { | 
 |   if (this->is_sentinel_) { | 
 |     assert(target->IsEmptyList() && | 
 |            "If target is not an empty list, the nodes in that list would not " | 
 |            "be linked to a sentinel."); | 
 |   } else { | 
 |     assert(IsInAList() && "The node being replaced must be in a list."); | 
 |     assert(!target->is_sentinel_ && | 
 |            "Cannot turn a sentinel node into one that is not."); | 
 |   } | 
 |  | 
 |   if (!this->IsEmptyList()) { | 
 |     // Link target into the same position that |this| was in. | 
 |     target->next_node_ = this->next_node_; | 
 |     target->previous_node_ = this->previous_node_; | 
 |     target->next_node_->previous_node_ = target; | 
 |     target->previous_node_->next_node_ = target; | 
 |  | 
 |     // Reset |this| to itself default value. | 
 |     if (!this->is_sentinel_) { | 
 |       // Reset |this| so that it is not in a list. | 
 |       this->next_node_ = nullptr; | 
 |       this->previous_node_ = nullptr; | 
 |     } else { | 
 |       // Set |this| so that it is the head of an empty list. | 
 |       // We cannot treat sentinel nodes like others because it is invalid for | 
 |       // a sentinel node to not be in a list. | 
 |       this->next_node_ = static_cast<NodeType*>(this); | 
 |       this->previous_node_ = static_cast<NodeType*>(this); | 
 |     } | 
 |   } else { | 
 |     // If |this| points to itself, it must be a sentinel node with an empty | 
 |     // list.  Reset |this| so that it is the head of an empty list.  We want | 
 |     // |target| to be the same.  The asserts above guarantee that. | 
 |   } | 
 | } | 
 |  | 
 | template <class NodeType> | 
 | bool IntrusiveNodeBase<NodeType>::IsEmptyList() { | 
 |   if (next_node_ == this) { | 
 |     assert(is_sentinel_ && | 
 |            "None sentinel nodes should never point to themselves."); | 
 |     assert(previous_node_ == this && | 
 |            "Inconsistency with the previous and next nodes."); | 
 |     return true; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | }  // namespace utils | 
 | }  // namespace spvtools | 
 |  | 
 | #endif  // SOURCE_UTIL_ILIST_NODE_H_ |