| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| file Copyright.txt or https://cmake.org/licensing for details. */ |
| #ifndef cmLinkedTree_h |
| #define cmLinkedTree_h |
| |
| #include "cmConfigure.h" // IWYU pragma: keep |
| |
| #include <assert.h> |
| #include <iterator> |
| #include <vector> |
| |
| /** |
| @brief A adaptor for traversing a tree structure in a vector |
| |
| This class is not intended to be wholly generic like a standard library |
| container adaptor. Mostly it exists to facilitate code sharing for the |
| needs of the cmState. For example, the Truncate() method is a specific |
| requirement of the cmState. |
| |
| An empty cmLinkedTree provides a Root() method, and an Push() method, |
| each of which return iterators. A Tree can be built up by extending |
| from the root, and then extending from any other iterator. |
| |
| An iterator resulting from this tree construction can be |
| forward-only-iterated toward the root. Extending the tree never |
| invalidates existing iterators. |
| */ |
| template <typename T> |
| class cmLinkedTree |
| { |
| typedef typename std::vector<T>::size_type PositionType; |
| typedef T* PointerType; |
| typedef T& ReferenceType; |
| |
| public: |
| class iterator : public std::iterator<std::forward_iterator_tag, T> |
| { |
| friend class cmLinkedTree; |
| cmLinkedTree* Tree; |
| |
| // The Position is always 'one past the end'. |
| PositionType Position; |
| |
| iterator(cmLinkedTree* tree, PositionType pos) |
| : Tree(tree) |
| , Position(pos) |
| { |
| } |
| |
| public: |
| iterator() |
| : Tree(nullptr) |
| , Position(0) |
| { |
| } |
| |
| void operator++() |
| { |
| assert(this->Tree); |
| assert(this->Tree->UpPositions.size() == this->Tree->Data.size()); |
| assert(this->Position <= this->Tree->Data.size()); |
| assert(this->Position > 0); |
| this->Position = this->Tree->UpPositions[this->Position - 1]; |
| } |
| |
| PointerType operator->() const |
| { |
| assert(this->Tree); |
| assert(this->Tree->UpPositions.size() == this->Tree->Data.size()); |
| assert(this->Position <= this->Tree->Data.size()); |
| assert(this->Position > 0); |
| return this->Tree->GetPointer(this->Position - 1); |
| } |
| |
| PointerType operator->() |
| { |
| assert(this->Tree); |
| assert(this->Tree->UpPositions.size() == this->Tree->Data.size()); |
| assert(this->Position <= this->Tree->Data.size()); |
| assert(this->Position > 0); |
| return this->Tree->GetPointer(this->Position - 1); |
| } |
| |
| ReferenceType operator*() const |
| { |
| assert(this->Tree); |
| assert(this->Tree->UpPositions.size() == this->Tree->Data.size()); |
| assert(this->Position <= this->Tree->Data.size()); |
| assert(this->Position > 0); |
| return this->Tree->GetReference(this->Position - 1); |
| } |
| |
| ReferenceType operator*() |
| { |
| assert(this->Tree); |
| assert(this->Tree->UpPositions.size() == this->Tree->Data.size()); |
| assert(this->Position <= this->Tree->Data.size()); |
| assert(this->Position > 0); |
| return this->Tree->GetReference(this->Position - 1); |
| } |
| |
| bool operator==(iterator other) const |
| { |
| assert(this->Tree); |
| assert(this->Tree->UpPositions.size() == this->Tree->Data.size()); |
| assert(this->Tree == other.Tree); |
| return this->Position == other.Position; |
| } |
| |
| bool operator!=(iterator other) const |
| { |
| assert(this->Tree); |
| assert(this->Tree->UpPositions.size() == this->Tree->Data.size()); |
| return !(*this == other); |
| } |
| |
| bool IsValid() const |
| { |
| if (!this->Tree) { |
| return false; |
| } |
| return this->Position <= this->Tree->Data.size(); |
| } |
| |
| bool StrictWeakOrdered(iterator other) const |
| { |
| assert(this->Tree); |
| assert(this->Tree == other.Tree); |
| return this->Position < other.Position; |
| } |
| }; |
| |
| iterator Root() const |
| { |
| return iterator(const_cast<cmLinkedTree*>(this), 0); |
| } |
| |
| iterator Push(iterator it) { return Push_impl(it, T()); } |
| |
| iterator Push(iterator it, T t) { return Push_impl(it, std::move(t)); } |
| |
| bool IsLast(iterator it) { return it.Position == this->Data.size(); } |
| |
| iterator Pop(iterator it) |
| { |
| assert(!this->Data.empty()); |
| assert(this->UpPositions.size() == this->Data.size()); |
| bool const isLast = this->IsLast(it); |
| ++it; |
| // If this is the last entry then no other entry can refer |
| // to it so we can drop its storage. |
| if (isLast) { |
| this->Data.pop_back(); |
| this->UpPositions.pop_back(); |
| } |
| return it; |
| } |
| |
| iterator Truncate() |
| { |
| assert(!this->UpPositions.empty()); |
| this->UpPositions.erase(this->UpPositions.begin() + 1, |
| this->UpPositions.end()); |
| assert(!this->Data.empty()); |
| this->Data.erase(this->Data.begin() + 1, this->Data.end()); |
| return iterator(this, 1); |
| } |
| |
| void Clear() |
| { |
| this->UpPositions.clear(); |
| this->Data.clear(); |
| } |
| |
| private: |
| T& GetReference(PositionType pos) { return this->Data[pos]; } |
| |
| T* GetPointer(PositionType pos) { return &this->Data[pos]; } |
| |
| iterator Push_impl(iterator it, T&& t) |
| { |
| assert(this->UpPositions.size() == this->Data.size()); |
| assert(it.Position <= this->UpPositions.size()); |
| this->UpPositions.push_back(it.Position); |
| this->Data.push_back(std::move(t)); |
| return iterator(this, this->UpPositions.size()); |
| } |
| |
| std::vector<T> Data; |
| std::vector<PositionType> UpPositions; |
| }; |
| |
| #endif |