You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
190 lines
5.1 KiB
190 lines
5.1 KiB
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file Copyright.txt 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;
|
|
};
|