Move tree navigation to a new Path class that doesn't have to be a template.

The path also holds a reference to the root node, and that allows important
iterator accessors like start() and stop() to have no conditional code. (When
the compiler is clever enough to remove it.)

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@120165 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Jakob Stoklund Olesen 2010-11-26 01:39:40 +00:00
parent 20a00c4f80
commit 706da9d8ca
2 changed files with 364 additions and 296 deletions

View File

@ -403,7 +403,7 @@ public:
/// subtree - Access the i'th subtree reference in a branch node. /// subtree - Access the i'th subtree reference in a branch node.
/// This depends on branch nodes storing the NodeRef array as their first /// This depends on branch nodes storing the NodeRef array as their first
/// member. /// member.
NodeRef &subtree(unsigned i) { NodeRef &subtree(unsigned i) const {
return reinterpret_cast<NodeRef*>(pip.getPointer())[i]; return reinterpret_cast<NodeRef*>(pip.getPointer())[i];
} }
@ -699,6 +699,161 @@ public:
}; };
//===----------------------------------------------------------------------===//
//--- Path ---//
//===----------------------------------------------------------------------===//
//
// A Path is used by iterators to represent a position in a B+-tree, and the
// path to get there from the root.
//
// The Path class also constains the tree navigation code that doesn't have to
// be templatized.
//
//===----------------------------------------------------------------------===//
class Path {
/// Entry - Each step in the path is a node pointer and an offset into that
/// node.
struct Entry {
void *node;
unsigned size;
unsigned offset;
Entry(void *Node, unsigned Size, unsigned Offset)
: node(Node), size(Size), offset(Offset) {}
Entry(NodeRef Node, unsigned Offset)
: node(&Node.subtree(0)), size(Node.size()), offset(Offset) {}
NodeRef &subtree(unsigned i) const {
return reinterpret_cast<NodeRef*>(node)[i];
}
};
/// path - The path entries, path[0] is the root node, path.back() is a leaf.
SmallVector<Entry, 4> path;
public:
// Node accessors.
template <typename NodeT> NodeT &node(unsigned Level) const {
return *reinterpret_cast<NodeT*>(path[Level].node);
}
unsigned size(unsigned Level) const { return path[Level].size; }
unsigned offset(unsigned Level) const { return path[Level].offset; }
unsigned &offset(unsigned Level) { return path[Level].offset; }
// Leaf accessors.
template <typename NodeT> NodeT &leaf() const {
return *reinterpret_cast<NodeT*>(path.back().node);
}
unsigned leafSize() const { return path.back().size; }
unsigned leafOffset() const { return path.back().offset; }
unsigned &leafOffset() { return path.back().offset; }
/// valid - Return true if path is at a valid node, not at end().
bool valid() const {
return !path.empty() && path.front().offset < path.front().size;
}
/// height - Return the height of the tree corresponding to this path.
/// This matches map->height in a full path.
unsigned height() const { return path.size() - 1; }
/// subtree - Get the subtree referenced from Level. When the path is
/// consistent, node(Level + 1) == subtree(Level).
/// @param Level 0..height-1. The leaves have no subtrees.
NodeRef &subtree(unsigned Level) const {
return path[Level].subtree(path[Level].offset);
}
/// push - Add entry to path.
/// @param Node Node to add, should be subtree(path.size()-1).
/// @param Offset Offset into Node.
void push(NodeRef Node, unsigned Offset) {
path.push_back(Entry(Node, Offset));
}
/// setSize - Set the size of a node both in the path and in the tree.
/// @param Level 0..height. Note that setting the root size won't change
/// map->rootSize.
/// @param Size New node size.
void setSize(unsigned Level, unsigned Size) {
path[Level].size = Size;
if (Level)
subtree(Level - 1).setSize(Size);
}
/// setRoot - Clear the path and set a new root node.
/// @param Node New root node.
/// @param Size New root size.
/// @param Offset Offset into root node.
void setRoot(void *Node, unsigned Size, unsigned Offset) {
path.clear();
path.push_back(Entry(Node, Size, Offset));
}
/// replaceRoot - Replace the current root node with two new entries after the
/// tree height has increased.
/// @param Root The new root node.
/// @param Size Number of entries in the new root.
/// @param Offsets Offsets into the root and first branch nodes.
void replaceRoot(void *Root, unsigned Size, IdxPair Offsets);
/// getLeftSibling - Get the left sibling node at Level, or a null NodeRef.
/// @param Level Get the sibling to node(Level).
/// @return Left sibling, or NodeRef().
NodeRef getLeftSibling(unsigned Level) const;
/// moveLeft - Move path to the left sibling at Level. Leave nodes below Level
/// unaltered.
/// @param Level Move node(Level).
void moveLeft(unsigned Level);
/// fillLeft - Grow path to Height by taking leftmost branches.
/// @param Height The target height.
void fillLeft(unsigned Height) {
while (height() < Height)
push(subtree(height()), 0);
}
/// getLeftSibling - Get the left sibling node at Level, or a null NodeRef.
/// @param Level Get the sinbling to node(Level).
/// @return Left sibling, or NodeRef().
NodeRef getRightSibling(unsigned Level) const;
/// moveRight - Move path to the left sibling at Level. Leave nodes below
/// Level unaltered.
/// @param Level Move node(Level).
void moveRight(unsigned Level);
/// atLastBranch - Return true if the path is at the last branch of the node
/// at Level.
/// @param Level Node to examine.
bool atLastBranch(unsigned Level) const {
return path[Level].offset == path[Level].size - 1;
}
/// legalizeForInsert - Prepare the path for an insertion at Level. When the
/// path is at end(), node(Level) may not be a legal node. legalizeForInsert
/// ensures that node(Level) is real by moving back to the last node at Level,
/// and setting offset(Level) to size(Level) if required.
/// @param Level The level where an insertion is about to take place.
void legalizeForInsert(unsigned Level) {
if (valid())
return;
moveLeft(Level);
++path[Level].offset;
}
#ifndef NDEBUG
void dump() const {
for (unsigned l = 0, e = path.size(); l != e; ++l)
errs() << l << ": " << path[l].node << ' ' << path[l].size << ' '
<< path[l].offset << '\n';
}
#endif
};
} // namespace IntervalMapImpl } // namespace IntervalMapImpl
@ -1103,107 +1258,54 @@ class IntervalMap<KeyT, ValT, N, Traits>::const_iterator :
public std::iterator<std::bidirectional_iterator_tag, ValT> { public std::iterator<std::bidirectional_iterator_tag, ValT> {
protected: protected:
friend class IntervalMap; friend class IntervalMap;
typedef std::pair<IntervalMapImpl::NodeRef, unsigned> PathEntry;
typedef SmallVector<PathEntry, 4> Path;
// The map referred to. // The map referred to.
IntervalMap *map; IntervalMap *map;
// The offset into map's root node.
unsigned rootOffset;
// We store a full path from the root to the current position. // We store a full path from the root to the current position.
//
// When rootOffset == map->rootSize, we are at end() and path() is empty.
// Otherwise, when branched these conditions hold:
//
// 1. path.front().first == rootBranch().subtree(rootOffset)
// 2. path[i].first == path[i-1].first.subtree(path[i-1].second)
// 3. path.size() == map->height.
//
// Thus, path.back() always refers to the current leaf node unless the root is
// unbranched.
//
// The path may be partially filled, but never between iterator calls. // The path may be partially filled, but never between iterator calls.
Path path; IntervalMapImpl::Path path;
explicit const_iterator(IntervalMap &map) explicit const_iterator(IntervalMap &map) : map(&map) {}
: map(&map), rootOffset(map.rootSize) {}
bool branched() const { bool branched() const {
assert(map && "Invalid iterator"); assert(map && "Invalid iterator");
return map->branched(); return map->branched();
} }
IntervalMapImpl::NodeRef pathNode(unsigned h) const { return path[h].first; } void setRoot(unsigned Offset) {
IntervalMapImpl::NodeRef &pathNode(unsigned h) { return path[h].first; } if (branched())
unsigned pathOffset(unsigned h) const { return path[h].second; } path.setRoot(&map->rootBranch(), map->rootSize, Offset);
unsigned &pathOffset(unsigned h) { return path[h].second; }
Leaf &treeLeaf() const {
assert(branched() && path.size() == map->height);
return path.back().first.template get<Leaf>();
}
unsigned treeLeafSize() const {
assert(branched() && path.size() == map->height);
return path.back().first.size();
}
unsigned &treeLeafOffset() {
assert(branched() && path.size() == map->height);
return path.back().second;
}
unsigned treeLeafOffset() const {
assert(branched() && path.size() == map->height);
return path.back().second;
}
// Get the next node ptr for an incomplete path.
IntervalMapImpl::NodeRef pathNextDown() {
assert(path.size() < map->height && "Path is already complete");
if (path.empty())
return map->rootBranch().subtree(rootOffset);
else else
return path.back().first.subtree(path.back().second); path.setRoot(&map->rootLeaf(), map->rootSize, Offset);
} }
void pathFillLeft();
void pathFillFind(KeyT x); void pathFillFind(KeyT x);
void pathFillRight();
IntervalMapImpl::NodeRef leftSibling(unsigned level) const;
IntervalMapImpl::NodeRef rightSibling(unsigned level) const;
void treeIncrement();
void treeDecrement();
void treeFind(KeyT x); void treeFind(KeyT x);
public: public:
/// valid - Return true if the current position is valid, false for end(). /// valid - Return true if the current position is valid, false for end().
bool valid() const { bool valid() const { return path.valid(); }
assert(map && "Invalid iterator");
return rootOffset < map->rootSize;
}
/// start - Return the beginning of the current interval. /// start - Return the beginning of the current interval.
const KeyT &start() const { const KeyT &start() const {
assert(valid() && "Cannot access invalid iterator"); assert(valid() && "Cannot access invalid iterator");
return branched() ? treeLeaf().start(treeLeafOffset()) : return branched() ? path.leaf<Leaf>().start(path.leafOffset()) :
map->rootLeaf().start(rootOffset); path.leaf<RootLeaf>().start(path.leafOffset());
} }
/// stop - Return the end of the current interval. /// stop - Return the end of the current interval.
const KeyT &stop() const { const KeyT &stop() const {
assert(valid() && "Cannot access invalid iterator"); assert(valid() && "Cannot access invalid iterator");
return branched() ? treeLeaf().stop(treeLeafOffset()) : return branched() ? path.leaf<Leaf>().stop(path.leafOffset()) :
map->rootLeaf().stop(rootOffset); path.leaf<RootLeaf>().stop(path.leafOffset());
} }
/// value - Return the mapped value at the current interval. /// value - Return the mapped value at the current interval.
const ValT &value() const { const ValT &value() const {
assert(valid() && "Cannot access invalid iterator"); assert(valid() && "Cannot access invalid iterator");
return branched() ? treeLeaf().value(treeLeafOffset()) : return branched() ? path.leaf<Leaf>().value(path.leafOffset()) :
map->rootLeaf().value(rootOffset); path.leaf<RootLeaf>().value(path.leafOffset());
} }
const ValT &operator*() const { const ValT &operator*() const {
@ -1212,8 +1314,11 @@ public:
bool operator==(const const_iterator &RHS) const { bool operator==(const const_iterator &RHS) const {
assert(map == RHS.map && "Cannot compare iterators from different maps"); assert(map == RHS.map && "Cannot compare iterators from different maps");
return rootOffset == RHS.rootOffset && if (!valid())
(!valid() || !branched() || path.back() == RHS.path.back()); return !RHS.valid();
if (path.leafOffset() != RHS.path.leafOffset())
return false;
return &path.template leaf<Leaf>() == &RHS.path.template leaf<Leaf>();
} }
bool operator!=(const const_iterator &RHS) const { bool operator!=(const const_iterator &RHS) const {
@ -1222,27 +1327,21 @@ public:
/// goToBegin - Move to the first interval in map. /// goToBegin - Move to the first interval in map.
void goToBegin() { void goToBegin() {
rootOffset = 0; setRoot(0);
path.clear();
if (branched()) if (branched())
pathFillLeft(); path.fillLeft(map->height);
} }
/// goToEnd - Move beyond the last interval in map. /// goToEnd - Move beyond the last interval in map.
void goToEnd() { void goToEnd() {
rootOffset = map->rootSize; setRoot(map->rootSize);
path.clear();
} }
/// preincrement - move to the next interval. /// preincrement - move to the next interval.
const_iterator &operator++() { const_iterator &operator++() {
assert(valid() && "Cannot increment end()"); assert(valid() && "Cannot increment end()");
if (!branched()) if (++path.leafOffset() == path.leafSize() && branched())
++rootOffset; path.moveRight(map->height);
else if (treeLeafOffset() != treeLeafSize() - 1)
++treeLeafOffset();
else
treeIncrement();
return *this; return *this;
} }
@ -1255,13 +1354,10 @@ public:
/// predecrement - move to the previous interval. /// predecrement - move to the previous interval.
const_iterator &operator--() { const_iterator &operator--() {
if (!branched()) { if (path.leafOffset() && (valid() || !branched()))
assert(rootOffset && "Cannot decrement begin()"); --path.leafOffset();
--rootOffset;
} else if (valid() && treeLeafOffset())
--treeLeafOffset();
else else
treeDecrement(); path.moveLeft(map->height);
return *this; return *this;
} }
@ -1278,7 +1374,7 @@ public:
if (branched()) if (branched())
treeFind(x); treeFind(x);
else else
rootOffset = map->rootLeaf().findFrom(0, map->rootSize, x); setRoot(map->rootLeaf().findFrom(0, map->rootSize, x));
} }
/// advanceTo - Move to the first interval with stop >= x, or end(). /// advanceTo - Move to the first interval with stop >= x, or end().
@ -1288,150 +1384,30 @@ public:
if (branched()) if (branched())
treeAdvanceTo(x); treeAdvanceTo(x);
else else
rootOffset = map->rootLeaf().findFrom(rootOffset, map->rootSize, x); path.leafOffset() =
map->rootLeaf().findFrom(path.leafOffset(), map->rootSize, x);
} }
}; };
// pathFillLeft - Complete path by following left-most branches.
template <typename KeyT, typename ValT, unsigned N, typename Traits>
void IntervalMap<KeyT, ValT, N, Traits>::
const_iterator::pathFillLeft() {
IntervalMapImpl::NodeRef NR = pathNextDown();
for (unsigned i = map->height - path.size() - 1; i; --i) {
path.push_back(PathEntry(NR, 0));
NR = NR.subtree(0);
}
path.push_back(PathEntry(NR, 0));
}
// pathFillFind - Complete path by searching for x. // pathFillFind - Complete path by searching for x.
template <typename KeyT, typename ValT, unsigned N, typename Traits> template <typename KeyT, typename ValT, unsigned N, typename Traits>
void IntervalMap<KeyT, ValT, N, Traits>:: void IntervalMap<KeyT, ValT, N, Traits>::
const_iterator::pathFillFind(KeyT x) { const_iterator::pathFillFind(KeyT x) {
IntervalMapImpl::NodeRef NR = pathNextDown(); IntervalMapImpl::NodeRef NR = path.subtree(path.height());
for (unsigned i = map->height - path.size() - 1; i; --i) { for (unsigned i = map->height - path.height() - 1; i; --i) {
unsigned p = NR.get<Branch>().safeFind(0, x); unsigned p = NR.get<Branch>().safeFind(0, x);
path.push_back(PathEntry(NR, p)); path.push(NR, p);
NR = NR.subtree(p); NR = NR.subtree(p);
} }
path.push_back(PathEntry(NR, NR.get<Leaf>().safeFind(0, x))); path.push(NR, NR.get<Leaf>().safeFind(0, x));
}
// pathFillRight - Complete path by adding rightmost entries.
template <typename KeyT, typename ValT, unsigned N, typename Traits>
void IntervalMap<KeyT, ValT, N, Traits>::
const_iterator::pathFillRight() {
IntervalMapImpl::NodeRef NR = pathNextDown();
for (unsigned i = map->height - path.size() - 1; i; --i) {
unsigned p = NR.size() - 1;
path.push_back(PathEntry(NR, p));
NR = NR.subtree(p);
}
path.push_back(PathEntry(NR, NR.size() - 1));
}
/// leftSibling - find the left sibling node to path[level].
/// @param level 0 is just below the root, map->height - 1 for the leaves.
/// @return The left sibling NodeRef, or NULL.
template <typename KeyT, typename ValT, unsigned N, typename Traits>
IntervalMapImpl::NodeRef IntervalMap<KeyT, ValT, N, Traits>::
const_iterator::leftSibling(unsigned level) const {
using namespace IntervalMapImpl;
assert(branched() && "Not at a branched node");
assert(level <= path.size() && "Bad level");
// Go up the tree until we can go left.
unsigned h = level;
while (h && pathOffset(h - 1) == 0)
--h;
// We are at the first leaf node, no left sibling.
if (!h && rootOffset == 0)
return NodeRef();
// NR is the subtree containing our left sibling.
NodeRef NR = h ?
pathNode(h - 1).subtree(pathOffset(h - 1) - 1) :
map->rootBranch().subtree(rootOffset - 1);
// Keep right all the way down.
for (; h != level; ++h)
NR = NR.subtree(NR.size() - 1);
return NR;
}
/// rightSibling - find the right sibling node to path[level].
/// @param level 0 is just below the root, map->height - 1 for the leaves.
/// @return The right sibling NodeRef, or NULL.
template <typename KeyT, typename ValT, unsigned N, typename Traits>
IntervalMapImpl::NodeRef IntervalMap<KeyT, ValT, N, Traits>::
const_iterator::rightSibling(unsigned level) const {
using namespace IntervalMapImpl;
assert(branched() && "Not at a branched node");
assert(level <= this->path.size() && "Bad level");
// Go up the tree until we can go right.
unsigned h = level;
while (h && pathOffset(h - 1) == pathNode(h - 1).size() - 1)
--h;
// We are at the last leaf node, no right sibling.
if (!h && rootOffset == map->rootSize - 1)
return NodeRef();
// NR is the subtree containing our right sibling.
NodeRef NR = h ?
pathNode(h - 1).subtree(pathOffset(h - 1) + 1) :
map->rootBranch().subtree(rootOffset + 1);
// Keep left all the way down.
for (; h != level; ++h)
NR = NR.subtree(0);
return NR;
}
// treeIncrement - Move to the beginning of the next leaf node.
template <typename KeyT, typename ValT, unsigned N, typename Traits>
void IntervalMap<KeyT, ValT, N, Traits>::
const_iterator::treeIncrement() {
assert(branched() && "treeIncrement is not for small maps");
assert(path.size() == map->height && "inconsistent iterator");
do path.pop_back();
while (!path.empty() && path.back().second == path.back().first.size() - 1);
if (path.empty()) {
++rootOffset;
if (!valid())
return;
} else
++path.back().second;
pathFillLeft();
}
// treeDecrement - Move to the end of the previous leaf node.
template <typename KeyT, typename ValT, unsigned N, typename Traits>
void IntervalMap<KeyT, ValT, N, Traits>::
const_iterator::treeDecrement() {
assert(branched() && "treeDecrement is not for small maps");
if (valid()) {
assert(path.size() == map->height && "inconsistent iterator");
do path.pop_back();
while (!path.empty() && path.back().second == 0);
}
if (path.empty()) {
assert(rootOffset && "cannot treeDecrement() on begin()");
--rootOffset;
} else
--path.back().second;
pathFillRight();
} }
// treeFind - Find in a branched tree. // treeFind - Find in a branched tree.
template <typename KeyT, typename ValT, unsigned N, typename Traits> template <typename KeyT, typename ValT, unsigned N, typename Traits>
void IntervalMap<KeyT, ValT, N, Traits>:: void IntervalMap<KeyT, ValT, N, Traits>::
const_iterator::treeFind(KeyT x) { const_iterator::treeFind(KeyT x) {
path.clear(); setRoot(map->rootBranch().findFrom(0, map->rootSize, x));
rootOffset = map->rootBranch().findFrom(0, map->rootSize, x);
if (valid()) if (valid())
pathFillFind(x); pathFillFind(x);
} }
@ -1488,7 +1464,6 @@ class IntervalMap<KeyT, ValT, N, Traits>::iterator : public const_iterator {
explicit iterator(IntervalMap &map) : const_iterator(map) {} explicit iterator(IntervalMap &map) : const_iterator(map) {}
void setNodeSize(unsigned Level, unsigned Size);
void setNodeStop(unsigned Level, KeyT Stop); void setNodeStop(unsigned Level, KeyT Stop);
bool insertNode(unsigned Level, IntervalMapImpl::NodeRef Node, KeyT Stop); bool insertNode(unsigned Level, IntervalMapImpl::NodeRef Node, KeyT Stop);
template <typename NodeT> bool overflow(unsigned Level); template <typename NodeT> bool overflow(unsigned Level);
@ -1500,31 +1475,22 @@ public:
}; };
/// setNodeSize - Set the size of the node at path[level], updating both path
/// and the real tree.
/// @param level 0 is just below the root, map->height - 1 for the leaves.
/// @param size New node size.
template <typename KeyT, typename ValT, unsigned N, typename Traits>
void IntervalMap<KeyT, ValT, N, Traits>::
iterator::setNodeSize(unsigned Level, unsigned Size) {
this->pathNode(Level).setSize(Size);
if (Level)
this->pathNode(Level-1).subtree(this->pathOffset(Level-1)).setSize(Size);
else
this->map->rootBranch().subtree(this->rootOffset).setSize(Size);
}
/// setNodeStop - Update the stop key of the current node at level and above. /// setNodeStop - Update the stop key of the current node at level and above.
template <typename KeyT, typename ValT, unsigned N, typename Traits> template <typename KeyT, typename ValT, unsigned N, typename Traits>
void IntervalMap<KeyT, ValT, N, Traits>:: void IntervalMap<KeyT, ValT, N, Traits>::
iterator::setNodeStop(unsigned Level, KeyT Stop) { iterator::setNodeStop(unsigned Level, KeyT Stop) {
while (Level--) { // There are no references to the root node, so nothing to update.
this->pathNode(Level).template get<Branch>() if (!Level)
.stop(this->pathOffset(Level)) = Stop; return;
if (this->pathOffset(Level) != this->pathNode(Level).size() - 1) IntervalMapImpl::Path &P = this->path;
// Update nodes pointing to the current node.
while (--Level) {
P.node<Branch>(Level).stop(P.offset(Level)) = Stop;
if (!P.atLastBranch(Level))
return; return;
} }
this->map->rootBranch().stop(this->rootOffset) = Stop; // Update root separately since it has a different layout.
P.node<RootBranch>(Level).stop(P.offset(Level)) = Stop;
} }
/// insertNode - insert a node before the current path at level. /// insertNode - insert a node before the current path at level.
@ -1536,42 +1502,40 @@ iterator::setNodeStop(unsigned Level, KeyT Stop) {
template <typename KeyT, typename ValT, unsigned N, typename Traits> template <typename KeyT, typename ValT, unsigned N, typename Traits>
bool IntervalMap<KeyT, ValT, N, Traits>:: bool IntervalMap<KeyT, ValT, N, Traits>::
iterator::insertNode(unsigned Level, IntervalMapImpl::NodeRef Node, KeyT Stop) { iterator::insertNode(unsigned Level, IntervalMapImpl::NodeRef Node, KeyT Stop) {
assert(Level && "Cannot insert next to the root");
bool SplitRoot = false; bool SplitRoot = false;
if (!Level) {
// Insert into the root branch node.
IntervalMap &IM = *this->map; IntervalMap &IM = *this->map;
IntervalMapImpl::Path &P = this->path;
if (Level == 1) {
// Insert into the root branch node.
if (IM.rootSize < RootBranch::Capacity) { if (IM.rootSize < RootBranch::Capacity) {
IM.rootBranch().insert(this->rootOffset, IM.rootSize, Node, Stop); IM.rootBranch().insert(P.offset(0), IM.rootSize, Node, Stop);
++IM.rootSize; P.setSize(0, ++IM.rootSize);
return SplitRoot; return SplitRoot;
} }
// We need to split the root while keeping our position. // We need to split the root while keeping our position.
SplitRoot = true; SplitRoot = true;
IdxPair Offset = IM.splitRoot(this->rootOffset); IdxPair Offset = IM.splitRoot(P.offset(0));
this->rootOffset = Offset.first; P.replaceRoot(&IM.rootBranch(), IM.rootSize, Offset);
this->path.insert(this->path.begin(),std::make_pair(
this->map->rootBranch().subtree(Offset.first), Offset.second)); // Fall through to insert at the new higher level.
Level = 1; ++Level;
} }
// When inserting before end(), make sure we have a valid path. // When inserting before end(), make sure we have a valid path.
if (!this->valid()) { P.legalizeForInsert(--Level);
this->treeDecrement();
++this->pathOffset(Level-1);
}
// Insert into the branch node at level-1. // Insert into the branch node at Level-1.
if (this->pathNode(Level-1).size() == Branch::Capacity) { if (P.size(Level) == Branch::Capacity) {
// Branch node is full, handle handle the overflow.
assert(!SplitRoot && "Cannot overflow after splitting the root"); assert(!SplitRoot && "Cannot overflow after splitting the root");
SplitRoot = overflow<Branch>(Level - 1); SplitRoot = overflow<Branch>(Level);
Level += SplitRoot; Level += SplitRoot;
} }
IntervalMapImpl::NodeRef NR = this->pathNode(Level-1); P.node<Branch>(Level).insert(P.offset(Level), P.size(Level), Node, Stop);
unsigned Offset = this->pathOffset(Level-1); P.setSize(Level, P.size(Level) + 1);
NR.get<Branch>().insert(Offset, NR.size(), Node, Stop);
setNodeSize(Level - 1, NR.size() + 1);
return SplitRoot; return SplitRoot;
} }
@ -1581,18 +1545,24 @@ void IntervalMap<KeyT, ValT, N, Traits>::
iterator::insert(KeyT a, KeyT b, ValT y) { iterator::insert(KeyT a, KeyT b, ValT y) {
if (this->branched()) if (this->branched())
return treeInsert(a, b, y); return treeInsert(a, b, y);
IdxPair IP = this->map->rootLeaf().insertFrom(this->rootOffset, IntervalMap &IM = *this->map;
this->map->rootSize, IntervalMapImpl::Path &P = this->path;
a, b, y);
// Try simple root leaf insert.
IdxPair IP = IM.rootLeaf().insertFrom(P.leafOffset(), IM.rootSize, a, b, y);
// Was the root node insert successful?
if (IP.second <= RootLeaf::Capacity) { if (IP.second <= RootLeaf::Capacity) {
this->rootOffset = IP.first; P.leafOffset() = IP.first;
this->map->rootSize = IP.second; P.setSize(0, IM.rootSize = IP.second);
return; return;
} }
IdxPair Offset = this->map->branchRoot(this->rootOffset);
this->rootOffset = Offset.first; // Root leaf node is full, we must branch.
this->path.push_back(std::make_pair( IdxPair Offset = IM.branchRoot(P.leafOffset());
this->map->rootBranch().subtree(Offset.first), Offset.second)); P.replaceRoot(&IM.rootBranch(), IM.rootSize, Offset);
// Now it fits in the new leaf.
treeInsert(a, b, y); treeInsert(a, b, y);
} }
@ -1600,29 +1570,26 @@ iterator::insert(KeyT a, KeyT b, ValT y) {
template <typename KeyT, typename ValT, unsigned N, typename Traits> template <typename KeyT, typename ValT, unsigned N, typename Traits>
void IntervalMap<KeyT, ValT, N, Traits>:: void IntervalMap<KeyT, ValT, N, Traits>::
iterator::treeInsert(KeyT a, KeyT b, ValT y) { iterator::treeInsert(KeyT a, KeyT b, ValT y) {
if (!this->valid()) { IntervalMap &IM = *this->map;
// end() has an empty path. Go back to the last leaf node and use an IntervalMapImpl::Path &P = this->path;
// invalid offset instead.
this->treeDecrement(); P.legalizeForInsert(IM.height);
++this->treeLeafOffset(); IdxPair IP = P.leaf<Leaf>().insertFrom(P.leafOffset(), P.leafSize(), a, b, y);
// Leaf insertion unsuccessful? Overflow and try again.
if (IP.second > Leaf::Capacity) {
overflow<Leaf>(IM.height);
IP = P.leaf<Leaf>().insertFrom(P.leafOffset(), P.leafSize(), a, b, y);
assert(IP.second <= Leaf::Capacity && "overflow() didn't make room");
} }
IdxPair IP = this->treeLeaf().insertFrom(this->treeLeafOffset(),
this->treeLeafSize(), a, b, y); // Inserted, update offset and leaf size.
this->treeLeafOffset() = IP.first; P.leafOffset() = IP.first;
if (IP.second <= Leaf::Capacity) { P.setSize(IM.height, IP.second);
setNodeSize(this->map->height - 1, IP.second);
// Insert was the last node entry, update stops.
if (IP.first == IP.second - 1) if (IP.first == IP.second - 1)
setNodeStop(this->map->height - 1, this->treeLeaf().stop(IP.first)); setNodeStop(IM.height, P.leaf<Leaf>().stop(IP.first));
return;
}
// Leaf node has no space.
overflow<Leaf>(this->map->height - 1);
IP = this->treeLeaf().insertFrom(this->treeLeafOffset(),
this->treeLeafSize(), a, b, y);
this->treeLeafOffset() = IP.first;
setNodeSize(this->map->height-1, IP.second);
if (IP.first == IP.second - 1)
setNodeStop(this->map->height - 1, this->treeLeaf().stop(IP.first));
// FIXME: Handle cross-node coalescing. // FIXME: Handle cross-node coalescing.
} }
@ -1638,26 +1605,26 @@ template <typename NodeT>
bool IntervalMap<KeyT, ValT, N, Traits>:: bool IntervalMap<KeyT, ValT, N, Traits>::
iterator::overflow(unsigned Level) { iterator::overflow(unsigned Level) {
using namespace IntervalMapImpl; using namespace IntervalMapImpl;
Path &P = this->path;
unsigned CurSize[4]; unsigned CurSize[4];
NodeT *Node[4]; NodeT *Node[4];
unsigned Nodes = 0; unsigned Nodes = 0;
unsigned Elements = 0; unsigned Elements = 0;
unsigned Offset = this->pathOffset(Level); unsigned Offset = P.offset(Level);
// Do we have a left sibling? // Do we have a left sibling?
NodeRef LeftSib = this->leftSibling(Level); NodeRef LeftSib = P.getLeftSibling(Level);
if (LeftSib) { if (LeftSib) {
Offset += Elements = CurSize[Nodes] = LeftSib.size(); Offset += Elements = CurSize[Nodes] = LeftSib.size();
Node[Nodes++] = &LeftSib.get<NodeT>(); Node[Nodes++] = &LeftSib.get<NodeT>();
} }
// Current node. // Current node.
NodeRef CurNode = this->pathNode(Level); Elements += CurSize[Nodes] = P.size(Level);
Elements += CurSize[Nodes] = CurNode.size(); Node[Nodes++] = &P.node<NodeT>(Level);
Node[Nodes++] = &CurNode.get<NodeT>();
// Do we have a right sibling? // Do we have a right sibling?
NodeRef RightSib = this->rightSibling(Level); NodeRef RightSib = P.getRightSibling(Level);
if (RightSib) { if (RightSib) {
Offset += Elements = CurSize[Nodes] = RightSib.size(); Offset += Elements = CurSize[Nodes] = RightSib.size();
Node[Nodes++] = &RightSib.get<NodeT>(); Node[Nodes++] = &RightSib.get<NodeT>();
@ -1682,7 +1649,7 @@ iterator::overflow(unsigned Level) {
// Move current location to the leftmost node. // Move current location to the leftmost node.
if (LeftSib) if (LeftSib)
this->treeDecrement(); P.moveLeft(Level);
// Move elements right. // Move elements right.
for (int n = Nodes - 1; n; --n) { for (int n = Nodes - 1; n; --n) {
@ -1728,21 +1695,21 @@ iterator::overflow(unsigned Level) {
SplitRoot = insertNode(Level, NodeRef(Node[Pos], NewSize[Pos]), Stop); SplitRoot = insertNode(Level, NodeRef(Node[Pos], NewSize[Pos]), Stop);
Level += SplitRoot; Level += SplitRoot;
} else { } else {
setNodeSize(Level, NewSize[Pos]); P.setSize(Level, NewSize[Pos]);
setNodeStop(Level, Stop); setNodeStop(Level, Stop);
} }
if (Pos + 1 == Nodes) if (Pos + 1 == Nodes)
break; break;
this->treeIncrement(); P.moveRight(Level);
++Pos; ++Pos;
} }
// Where was I? Find NewOffset. // Where was I? Find NewOffset.
while(Pos != NewOffset.first) { while(Pos != NewOffset.first) {
this->treeDecrement(); P.moveLeft(Level);
--Pos; --Pos;
} }
this->pathOffset(Level) = NewOffset.second; P.offset(Level) = NewOffset.second;
return SplitRoot; return SplitRoot;
} }

View File

@ -16,6 +16,107 @@
namespace llvm { namespace llvm {
namespace IntervalMapImpl { namespace IntervalMapImpl {
void Path::replaceRoot(void *Root, unsigned Size, IdxPair Offsets) {
assert(!path.empty() && "Can't replace missing root");
path.front() = Entry(Root, Size, Offsets.first);
path.insert(path.begin() + 1, Entry(subtree(0), Offsets.second));
}
NodeRef Path::getLeftSibling(unsigned Level) const {
// The root has no siblings.
if (Level == 0)
return NodeRef();
// Go up the tree until we can go left.
unsigned l = Level - 1;
while (l && path[l].offset == 0)
--l;
// We can't go left.
if (path[l].offset == 0)
return NodeRef();
// NR is the subtree containing our left sibling.
NodeRef NR = path[l].subtree(path[l].offset - 1);
// Keep right all the way down.
for (++l; l != Level; ++l)
NR = NR.subtree(NR.size() - 1);
return NR;
}
void Path::moveLeft(unsigned Level) {
assert(Level != 0 && "Cannot move the root node");
// Go up the tree until we can go left.
unsigned l = 0;
if (valid()) {
l = Level - 1;
while (path[l].offset == 0) {
assert(l != 0 && "Cannot move beyond begin()");
--l;
}
} else if (height() < Level)
// end() may have created a height=0 path.
path.resize(Level + 1, Entry(0, 0, 0));
// NR is the subtree containing our left sibling.
--path[l].offset;
NodeRef NR = subtree(l);
// Get the rightmost node in the subtree.
for (++l; l != Level; ++l) {
path[l] = Entry(NR, NR.size() - 1);
NR = NR.subtree(NR.size() - 1);
}
path[l] = Entry(NR, NR.size() - 1);
}
NodeRef Path::getRightSibling(unsigned Level) const {
// The root has no siblings.
if (Level == 0)
return NodeRef();
// Go up the tree until we can go right.
unsigned l = Level - 1;
while (l && atLastBranch(l))
--l;
// We can't go right.
if (atLastBranch(l))
return NodeRef();
// NR is the subtree containing our right sibling.
NodeRef NR = path[l].subtree(path[l].offset + 1);
// Keep left all the way down.
for (++l; l != Level; ++l)
NR = NR.subtree(0);
return NR;
}
void Path::moveRight(unsigned Level) {
assert(Level != 0 && "Cannot move the root node");
// Go up the tree until we can go right.
unsigned l = Level - 1;
while (l && atLastBranch(l))
--l;
// NR is the subtree containing our right sibling. If we hit end(), we have
// offset(0) == node(0).size().
if (++path[l].offset == path[l].size)
return;
NodeRef NR = subtree(l);
for (++l; l != Level; ++l) {
path[l] = Entry(NR, 0);
NR = NR.subtree(0);
}
path[l] = Entry(NR, 0);
}
IdxPair distribute(unsigned Nodes, unsigned Elements, unsigned Capacity, IdxPair distribute(unsigned Nodes, unsigned Elements, unsigned Capacity,
const unsigned *CurSize, unsigned NewSize[], const unsigned *CurSize, unsigned NewSize[],
unsigned Position, bool Grow) { unsigned Position, bool Grow) {