Home | History | Annotate | Download | only in util
      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_H_
     16 #define SOURCE_UTIL_ILIST_H_
     17 
     18 #include <cassert>
     19 #include <memory>
     20 #include <type_traits>
     21 #include <vector>
     22 
     23 #include "source/util/ilist_node.h"
     24 
     25 namespace spvtools {
     26 namespace utils {
     27 
     28 // An IntrusiveList is a generic implementation of a doubly-linked list.  The
     29 // intended convention for using this container is:
     30 //
     31 //      class Node : public IntrusiveNodeBase<Node> {
     32 //        // Note that "Node", the class being defined is the template.
     33 //        // Must have a default constructor accessible to List.
     34 //        // Add whatever data is needed in the node
     35 //      };
     36 //
     37 //      using List = IntrusiveList<Node>;
     38 //
     39 // You can also inherit from IntrusiveList instead of a typedef if you want to
     40 // add more functionality.
     41 //
     42 // The condition on the template for IntrusiveNodeBase is there to add some type
     43 // checking to the container.  The compiler will still allow inserting elements
     44 // of type IntrusiveNodeBase<Node>, but that would be an error. This assumption
     45 // allows NextNode and PreviousNode to return pointers to Node, and casting will
     46 // not be required by the user.
     47 
     48 template <class NodeType>
     49 class IntrusiveList {
     50  public:
     51   static_assert(
     52       std::is_base_of<IntrusiveNodeBase<NodeType>, NodeType>::value,
     53       "The type from the node must be derived from IntrusiveNodeBase, with "
     54       "itself in the template.");
     55 
     56   // Creates an empty list.
     57   inline IntrusiveList();
     58 
     59   // Moves the contents of the given list to the list being constructed.
     60   IntrusiveList(IntrusiveList&&);
     61 
     62   // Destorys the list.  Note that the elements of the list will not be deleted,
     63   // but they will be removed from the list.
     64   virtual ~IntrusiveList();
     65 
     66   // Moves all of the elements in the list on the RHS to the list on the LHS.
     67   IntrusiveList& operator=(IntrusiveList&&);
     68 
     69   // Basetype for iterators so an IntrusiveList can be traversed like STL
     70   // containers.
     71   template <class T>
     72   class iterator_template {
     73    public:
     74     iterator_template(const iterator_template& i) : node_(i.node_) {}
     75 
     76     iterator_template& operator++() {
     77       node_ = node_->next_node_;
     78       return *this;
     79     }
     80 
     81     iterator_template& operator--() {
     82       node_ = node_->previous_node_;
     83       return *this;
     84     }
     85 
     86     iterator_template& operator=(const iterator_template& i) {
     87       node_ = i.node_;
     88       return *this;
     89     }
     90 
     91     T& operator*() const { return *node_; }
     92     T* operator->() const { return node_; }
     93 
     94     friend inline bool operator==(const iterator_template& lhs,
     95                                   const iterator_template& rhs) {
     96       return lhs.node_ == rhs.node_;
     97     }
     98     friend inline bool operator!=(const iterator_template& lhs,
     99                                   const iterator_template& rhs) {
    100       return !(lhs == rhs);
    101     }
    102 
    103     // Moves the nodes in |list| to the list that |this| points to.  The
    104     // positions of the nodes will be immediately before the element pointed to
    105     // by the iterator.  The return value will be an iterator pointing to the
    106     // first of the newly inserted elements.
    107     iterator_template MoveBefore(IntrusiveList* list) {
    108       if (list->empty()) return *this;
    109 
    110       NodeType* first_node = list->sentinel_.next_node_;
    111       NodeType* last_node = list->sentinel_.previous_node_;
    112 
    113       this->node_->previous_node_->next_node_ = first_node;
    114       first_node->previous_node_ = this->node_->previous_node_;
    115 
    116       last_node->next_node_ = this->node_;
    117       this->node_->previous_node_ = last_node;
    118 
    119       list->sentinel_.next_node_ = &list->sentinel_;
    120       list->sentinel_.previous_node_ = &list->sentinel_;
    121 
    122       return iterator(first_node);
    123     }
    124 
    125     // Define standard iterator types needs so this class can be
    126     // used with <algorithms>.
    127     using iterator_category = std::bidirectional_iterator_tag;
    128     using difference_type = std::ptrdiff_t;
    129     using value_type = T;
    130     using pointer = T*;
    131     using const_pointer = const T*;
    132     using reference = T&;
    133     using const_reference = const T&;
    134     using size_type = size_t;
    135 
    136    protected:
    137     iterator_template() = delete;
    138     inline iterator_template(T* node) { node_ = node; }
    139     T* node_;
    140 
    141     friend IntrusiveList;
    142   };
    143 
    144   using iterator = iterator_template<NodeType>;
    145   using const_iterator = iterator_template<const NodeType>;
    146 
    147   // Various types of iterators for the start (begin) and one past the end (end)
    148   // of the list.
    149   //
    150   // Decrementing |end()| iterator will give and iterator pointing to the last
    151   // element in the list, if one exists.
    152   //
    153   // Incrementing |end()| iterator will give |begin()|.
    154   //
    155   // Decrementing |begin()| will give |end()|.
    156   //
    157   // TODO: Not marking these functions as noexcept because Visual Studio 2013
    158   // does not support it.  When we no longer care about that compiler, we should
    159   // mark these as noexcept.
    160   iterator begin();
    161   iterator end();
    162   const_iterator begin() const;
    163   const_iterator end() const;
    164   const_iterator cbegin() const;
    165   const_iterator cend() const;
    166 
    167   // Appends |node| to the end of the list.  If |node| is already in a list, it
    168   // will be removed from that list first.
    169   void push_back(NodeType* node);
    170 
    171   // Returns true if the list is empty.
    172   bool empty() const;
    173 
    174   // Makes the current list empty.
    175   inline void clear();
    176 
    177   // Returns references to the first or last element in the list.  It is an
    178   // error to call these functions on an empty list.
    179   NodeType& front();
    180   NodeType& back();
    181   const NodeType& front() const;
    182   const NodeType& back() const;
    183 
    184   // Transfers [|first|, |last|) from |other| into the list at |where|.
    185   //
    186   // If |other| is |this|, no change is made.
    187   void Splice(iterator where, IntrusiveList<NodeType>* other, iterator first,
    188               iterator last);
    189 
    190  protected:
    191   // Doing a deep copy of the list does not make sense if the list does not own
    192   // the data.  It is not clear who will own the newly created data.  Making
    193   // copies illegal for that reason.
    194   IntrusiveList(const IntrusiveList&) = delete;
    195   IntrusiveList& operator=(const IntrusiveList&) = delete;
    196 
    197   // This function will assert if it finds the list containing |node| is not in
    198   // a valid state.
    199   static void Check(NodeType* node);
    200 
    201   // A special node used to represent both the start and end of the list,
    202   // without being part of the list.
    203   NodeType sentinel_;
    204 };
    205 
    206 // Implementation of IntrusiveList
    207 
    208 template <class NodeType>
    209 inline IntrusiveList<NodeType>::IntrusiveList() : sentinel_() {
    210   sentinel_.next_node_ = &sentinel_;
    211   sentinel_.previous_node_ = &sentinel_;
    212   sentinel_.is_sentinel_ = true;
    213 }
    214 
    215 template <class NodeType>
    216 IntrusiveList<NodeType>::IntrusiveList(IntrusiveList&& list) : sentinel_() {
    217   sentinel_.next_node_ = &sentinel_;
    218   sentinel_.previous_node_ = &sentinel_;
    219   sentinel_.is_sentinel_ = true;
    220   list.sentinel_.ReplaceWith(&sentinel_);
    221 }
    222 
    223 template <class NodeType>
    224 IntrusiveList<NodeType>::~IntrusiveList() {
    225   clear();
    226 }
    227 
    228 template <class NodeType>
    229 IntrusiveList<NodeType>& IntrusiveList<NodeType>::operator=(
    230     IntrusiveList<NodeType>&& list) {
    231   list.sentinel_.ReplaceWith(&sentinel_);
    232   return *this;
    233 }
    234 
    235 template <class NodeType>
    236 inline typename IntrusiveList<NodeType>::iterator
    237 IntrusiveList<NodeType>::begin() {
    238   return iterator(sentinel_.next_node_);
    239 }
    240 
    241 template <class NodeType>
    242 inline typename IntrusiveList<NodeType>::iterator
    243 IntrusiveList<NodeType>::end() {
    244   return iterator(&sentinel_);
    245 }
    246 
    247 template <class NodeType>
    248 inline typename IntrusiveList<NodeType>::const_iterator
    249 IntrusiveList<NodeType>::begin() const {
    250   return const_iterator(sentinel_.next_node_);
    251 }
    252 
    253 template <class NodeType>
    254 inline typename IntrusiveList<NodeType>::const_iterator
    255 IntrusiveList<NodeType>::end() const {
    256   return const_iterator(&sentinel_);
    257 }
    258 
    259 template <class NodeType>
    260 inline typename IntrusiveList<NodeType>::const_iterator
    261 IntrusiveList<NodeType>::cbegin() const {
    262   return const_iterator(sentinel_.next_node_);
    263 }
    264 
    265 template <class NodeType>
    266 inline typename IntrusiveList<NodeType>::const_iterator
    267 IntrusiveList<NodeType>::cend() const {
    268   return const_iterator(&sentinel_);
    269 }
    270 
    271 template <class NodeType>
    272 void IntrusiveList<NodeType>::push_back(NodeType* node) {
    273   node->InsertBefore(&sentinel_);
    274 }
    275 
    276 template <class NodeType>
    277 bool IntrusiveList<NodeType>::empty() const {
    278   return sentinel_.NextNode() == nullptr;
    279 }
    280 
    281 template <class NodeType>
    282 void IntrusiveList<NodeType>::clear() {
    283   while (!empty()) {
    284     front().RemoveFromList();
    285   }
    286 }
    287 
    288 template <class NodeType>
    289 NodeType& IntrusiveList<NodeType>::front() {
    290   NodeType* node = sentinel_.NextNode();
    291   assert(node != nullptr && "Can't get the front of an empty list.");
    292   return *node;
    293 }
    294 
    295 template <class NodeType>
    296 NodeType& IntrusiveList<NodeType>::back() {
    297   NodeType* node = sentinel_.PreviousNode();
    298   assert(node != nullptr && "Can't get the back of an empty list.");
    299   return *node;
    300 }
    301 
    302 template <class NodeType>
    303 const NodeType& IntrusiveList<NodeType>::front() const {
    304   NodeType* node = sentinel_.NextNode();
    305   assert(node != nullptr && "Can't get the front of an empty list.");
    306   return *node;
    307 }
    308 
    309 template <class NodeType>
    310 const NodeType& IntrusiveList<NodeType>::back() const {
    311   NodeType* node = sentinel_.PreviousNode();
    312   assert(node != nullptr && "Can't get the back of an empty list.");
    313   return *node;
    314 }
    315 
    316 template <class NodeType>
    317 void IntrusiveList<NodeType>::Splice(iterator where,
    318                                      IntrusiveList<NodeType>* other,
    319                                      iterator first, iterator last) {
    320   if (first == last) return;
    321   if (other == this) return;
    322 
    323   NodeType* first_prev = first.node_->previous_node_;
    324   NodeType* where_next = where.node_->next_node_;
    325 
    326   // Attach first.
    327   where.node_->next_node_ = first.node_;
    328   first.node_->previous_node_ = where.node_;
    329 
    330   // Attach last.
    331   where_next->previous_node_ = last.node_->previous_node_;
    332   last.node_->previous_node_->next_node_ = where_next;
    333 
    334   // Fixup other.
    335   first_prev->next_node_ = last.node_;
    336   last.node_->previous_node_ = first_prev;
    337 }
    338 
    339 template <class NodeType>
    340 void IntrusiveList<NodeType>::Check(NodeType* start) {
    341   int sentinel_count = 0;
    342   NodeType* p = start;
    343   do {
    344     assert(p != nullptr);
    345     assert(p->next_node_->previous_node_ == p);
    346     assert(p->previous_node_->next_node_ == p);
    347     if (p->is_sentinel_) sentinel_count++;
    348     p = p->next_node_;
    349   } while (p != start);
    350   assert(sentinel_count == 1 && "List should have exactly 1 sentinel node.");
    351 
    352   p = start;
    353   do {
    354     assert(p != nullptr);
    355     assert(p->previous_node_->next_node_ == p);
    356     assert(p->next_node_->previous_node_ == p);
    357     if (p->is_sentinel_) sentinel_count++;
    358     p = p->previous_node_;
    359   } while (p != start);
    360 }
    361 
    362 }  // namespace utils
    363 }  // namespace spvtools
    364 
    365 #endif  // SOURCE_UTIL_ILIST_H_
    366