| // 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_ |