| // Copyright 2012 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. |
| // |
| // Author: morlovich@google.com (Maksim Orlovich) |
| // |
| // This contains a simple linked list that's optimized for memory usage, |
| // cheap appends and traversals (including removals). Links |
| // are stored within elements rather than externally. |
| |
| #ifndef PAGESPEED_KERNEL_BASE_INLINE_SLIST_H_ |
| #define PAGESPEED_KERNEL_BASE_INLINE_SLIST_H_ |
| |
| #include <cstddef> |
| |
| #include "base/logging.h" // for DCHECK |
| #include "pagespeed/kernel/base/basictypes.h" |
| |
| namespace net_instaweb { |
| |
| // This forward declaration is necessary! Ignore IWYU when it tells you to |
| // remove it. Sadly, the pragma is being ignored right now. |
| template<class T> class InlineSList; |
| |
| // A helper base class for things that would get stored in the list. |
| // You don't have to inherit this, and can implement next() and set_next() |
| // directly. |
| template<class T> |
| class InlineSListElement { |
| protected: |
| InlineSListElement() : next_(NULL) {} |
| |
| private: |
| friend class InlineSList<T>; |
| T* next() { return next_; } |
| void set_next(T* new_next) { next_ = new_next; } |
| |
| T* next_; |
| DISALLOW_COPY_AND_ASSIGN(InlineSListElement); |
| }; |
| |
| // A simple linked list that's optimized for memory usage, |
| // cheap appends and traversals (including removals). Links |
| // are stored within elements rather than externally. |
| // |
| // To permit that, the type T must provide next() and set_next() methods, |
| // accessible to InlineSList<T>. Easy way to do that is by inheriting off |
| // InlineSListElement<T>. |
| // |
| // Note that while this results in a list object that's just one pointer wide, |
| // iterators are two pointers wide. |
| // |
| // Representation: circular linked list with a pointer to tail. Iterators |
| // store pointers to nodes before the one they're conceptually targeting. |
| template<class T> |
| class InlineSList { |
| private: |
| // (Unfortunately, this private class has to be above the public: section |
| // since public classes inherit off it). |
| // |
| // We represent an iterator by keeping a pointer to the node before |
| // the one it represents, which makes it easy to delete things. |
| // |
| // We also keep a pointer to the containing list, so we can |
| // detect when we walk past the end of the list, at which point we |
| // turn the node_ pointer into NULL. (Which also means the begin |
| // iterator for an empty list is a one-past-end iterator, as expected). |
| class IterBase { |
| protected: |
| IterBase(const InlineSList<T>* list, T* node) |
| : list_(list), node_(node) { |
| } |
| |
| bool AtEnd() const { |
| return (node_ == NULL); |
| } |
| |
| void Advance() { |
| DCHECK(!AtEnd()); |
| node_ = node_->next(); |
| // If we travel to the tail node (as opposed to start pointing to it), |
| // we have reached the end, and became one-past-the-end iterator. |
| if (node_ == list_->tail_) { |
| node_ = NULL; |
| } |
| } |
| |
| T* Data() { |
| return node_->next(); |
| } |
| |
| bool Equals(const IterBase& other) const { |
| return (node_ == other.node_) && (list_ == other.list_); |
| } |
| |
| private: |
| friend class InlineSList<T>; |
| const InlineSList<T>* list_; |
| T* node_; |
| }; |
| |
| public: |
| // Iterator interface to the list contents. You may use this both for simple |
| // enumeration and for deletion. Iteration works the same as with any STL |
| // container. |
| // |
| // If you want to remove things, make sure not to call the operator ++ |
| // when you do, as after deletion the iterator will be pointing at the next |
| // element already (or past the end!). An example of doing it right: |
| // |
| // InlineSList<Type>::iterator iter(list.begin()); |
| // while (iter != list.end()) { |
| // if (ShouldErase(*iter)) { |
| // list.Erase(&iter); |
| // } else { |
| // ++iter; |
| // } |
| // } |
| // |
| // This type does not make general guarantees of iterators staying valid on |
| // operations --- only the iterator passed to Erase() will be fixed, not |
| // any others; and Append operations should not be done concurrent with |
| // iteration. |
| class Iterator : public IterBase { |
| public: |
| Iterator& operator++() { |
| this->Advance(); |
| return *this; |
| } |
| |
| T* Get() { return this->Data(); } |
| T* operator->() { return this->Data(); } |
| T& operator*() { return *this->Data(); } |
| bool operator==(const Iterator& other) const { return this->Equals(other); } |
| bool operator!=(const Iterator& other) const { |
| return !this->Equals(other); |
| } |
| // default copy op, dtor are OK. |
| |
| private: |
| friend class InlineSList<T>; |
| Iterator(const InlineSList<T>* list, T* prev) : IterBase(list, prev) {} |
| }; |
| |
| typedef Iterator iterator; |
| |
| // Read-only iterator type; cannot be used for deletion or to modify |
| // the contained items. |
| class ConstIterator : public IterBase { |
| public: |
| ConstIterator& operator++() { |
| this->Advance(); |
| return *this; |
| } |
| |
| const T* Get() { return this->Data(); } |
| const T* operator->() { return this->Data(); } |
| const T& operator*() { return *this->Data(); } |
| bool operator==(const ConstIterator& other) const { |
| return this->Equals(other); |
| } |
| bool operator!=(const ConstIterator& other) const { |
| return !this->Equals(other); |
| } |
| // default copy op, dtor are OK. |
| |
| private: |
| friend class InlineSList<T>; |
| ConstIterator(const InlineSList<T>* list, T* prev) : IterBase(list, prev) {} |
| }; |
| |
| typedef ConstIterator const_iterator; |
| |
| InlineSList() : tail_(NULL) { |
| } |
| |
| // The destructor deletes all the nodes in the list. |
| ~InlineSList(); |
| |
| bool IsEmpty() const { |
| return (tail_ == NULL); |
| } |
| |
| void Append(T* node); |
| |
| // Removes the item pointed to by the iterator, and updates the iterator |
| // to point after it. Note that this means that it is now effectively |
| // advanced (potentially past the end of the list) and that you should not |
| // call ++ if you just want to consume one item. |
| // See the iterator docs for example of proper use. |
| void Erase(Iterator* iter); |
| |
| // Returns last item. |
| T* Last() { |
| DCHECK(!IsEmpty()); |
| return tail_; |
| } |
| |
| const T* Last() const { |
| DCHECK(!IsEmpty()); |
| return tail_; |
| } |
| |
| // Iterator interface. |
| |
| // Note that all of these pass tail_ since iterator implementation internally |
| // keeps track of the /previous/ node to the one pointed at. |
| iterator begin() { return Iterator(this, tail_); } |
| const_iterator begin() const { return ConstIterator(this, tail_); } |
| |
| // End iterators have their position at NULL. |
| iterator end() { return Iterator(this, NULL); } |
| const_iterator end() const { return ConstIterator(this, NULL); } |
| |
| private: |
| // The representation we chose here is a circular linked list where we point |
| // at the tail. This is because that permits us to append in O(1) to the end, |
| // yet still have easy front-to-end traversal. |
| T* tail_; |
| |
| DISALLOW_COPY_AND_ASSIGN(InlineSList); |
| }; |
| |
| template<class T> |
| inline InlineSList<T>::~InlineSList() { |
| if (tail_ != NULL) { |
| T* node = tail_->next(); // start at head node. |
| while (true) { |
| T* next = node->next(); |
| delete node; |
| if (node == tail_) { // stop when we deleted tail. |
| break; |
| } else { |
| node = next; |
| } |
| } |
| } |
| tail_ = NULL; |
| } |
| |
| template<class T> |
| inline void InlineSList<T>::Append(T* node) { |
| if (tail_ == NULL) { |
| tail_ = node; |
| node->set_next(node); |
| } else { |
| node->set_next(tail_->next()); |
| tail_->set_next(node); |
| tail_ = node; |
| } |
| } |
| |
| template<class T> |
| inline void InlineSList<T>::Erase(Iterator* iter) { |
| DCHECK(!iter->AtEnd()); |
| |
| T* iter_node = iter->node_; |
| T* target_node = iter_node->next(); |
| |
| if (iter_node == target_node) { |
| // Only 1 element before the call, 0 now. |
| tail_ = NULL; |
| iter->node_ = NULL; |
| } else { |
| iter_node->set_next(target_node->next()); |
| if (target_node == tail_) { |
| // Removed tail.. need to point it earlier. |
| tail_ = iter_node; |
| // Iterator is now one-past-end |
| iter->node_ = NULL; |
| } |
| } |
| delete target_node; |
| } |
| |
| } // namespace net_instaweb |
| |
| #endif // PAGESPEED_KERNEL_BASE_INLINE_SLIST_H_ |