| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #pragma once | |
| #include "cmConfigure.h" // IWYU pragma: keep | |
| #include <cassert> | |
| #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 | |
| { | |
| using PositionType = typename std::vector<T>::size_type; | |
| using PointerType = T*; | |
| using ReferenceType = T&; | |
| public: | |
| class iterator | |
| { | |
| 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 this->Push_impl(it, T()); } | |
| iterator Push(iterator it, T t) { return this->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; | |
| }; |