mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 18:54:02 +01:00
Add test case with randomly ordered insertions, massive coalescing.
Implement iterator::erase() in a simple version that erases nodes when they become empty, but doesn't try to redistribute elements among siblings for better packing. Handle coalescing across leaf nodes which may require erasing entries. llvm-svn: 120226
This commit is contained in:
parent
45cd9713f2
commit
c6f16354b9
@ -243,6 +243,13 @@ public:
|
||||
moveLeft(j, i, Size - j);
|
||||
}
|
||||
|
||||
/// erase - Erase element at i.
|
||||
/// @param i Index of element to erase.
|
||||
/// @param Size Number of elements in node.
|
||||
void erase(unsigned i, unsigned Size) {
|
||||
erase(i, i+1, Size);
|
||||
}
|
||||
|
||||
/// shift - Shift elements [i;size) 1 position to the right.
|
||||
/// @param i Beginning of the range to move.
|
||||
/// @param Size Number of elements in node.
|
||||
@ -304,10 +311,8 @@ void adjustSiblingSizes(NodeT *Node[], unsigned Nodes,
|
||||
unsigned CurSize[], const unsigned NewSize[]) {
|
||||
// Move elements right.
|
||||
for (int n = Nodes - 1; n; --n) {
|
||||
if (CurSize[n] == NewSize[n]) {
|
||||
--Nodes;
|
||||
if (CurSize[n] == NewSize[n])
|
||||
continue;
|
||||
}
|
||||
for (int m = n - 1; m != -1; --m) {
|
||||
int d = Node[n]->adjustFromLeftSib(CurSize[n], *Node[m], CurSize[m],
|
||||
NewSize[n] - CurSize[n]);
|
||||
@ -1042,23 +1047,15 @@ private:
|
||||
KeyT rootBranchStart() const { return rootBranchData().start; }
|
||||
KeyT &rootBranchStart() { return rootBranchData().start; }
|
||||
|
||||
Leaf *allocLeaf() {
|
||||
return new(allocator.template Allocate<Leaf>()) Leaf();
|
||||
}
|
||||
void deleteLeaf(Leaf *P) {
|
||||
P->~Leaf();
|
||||
allocator.Deallocate(P);
|
||||
template <typename NodeT> NodeT *newNode() {
|
||||
return new(allocator.template Allocate<NodeT>()) NodeT();
|
||||
}
|
||||
|
||||
Branch *allocBranch() {
|
||||
return new(allocator.template Allocate<Branch>()) Branch();
|
||||
}
|
||||
void deleteBranch(Branch *P) {
|
||||
P->~Branch();
|
||||
template <typename NodeT> void deleteNode(NodeT *P) {
|
||||
P->~NodeT();
|
||||
allocator.Deallocate(P);
|
||||
}
|
||||
|
||||
|
||||
IdxPair branchRoot(unsigned Position);
|
||||
IdxPair splitRoot(unsigned Position);
|
||||
|
||||
@ -1217,8 +1214,9 @@ branchRoot(unsigned Position) {
|
||||
unsigned pos = 0;
|
||||
NodeRef node[Nodes];
|
||||
for (unsigned n = 0; n != Nodes; ++n) {
|
||||
node[n] = NodeRef(allocLeaf(), size[n]);
|
||||
node[n].template get<Leaf>().copy(rootLeaf(), pos, 0, size[n]);
|
||||
Leaf *L = newNode<Leaf>();
|
||||
L->copy(rootLeaf(), pos, 0, size[n]);
|
||||
node[n] = NodeRef(L, size[n]);
|
||||
pos += size[n];
|
||||
}
|
||||
|
||||
@ -1257,8 +1255,9 @@ splitRoot(unsigned Position) {
|
||||
unsigned Pos = 0;
|
||||
NodeRef Node[Nodes];
|
||||
for (unsigned n = 0; n != Nodes; ++n) {
|
||||
Node[n] = NodeRef(allocBranch(), Size[n]);
|
||||
Node[n].template get<Branch>().copy(rootBranch(), Pos, 0, Size[n]);
|
||||
Branch *B = newNode<Branch>();
|
||||
B->copy(rootBranch(), Pos, 0, Size[n]);
|
||||
Node[n] = NodeRef(B, Size[n]);
|
||||
Pos += Size[n];
|
||||
}
|
||||
|
||||
@ -1303,9 +1302,9 @@ template <typename KeyT, typename ValT, unsigned N, typename Traits>
|
||||
void IntervalMap<KeyT, ValT, N, Traits>::
|
||||
deleteNode(IntervalMapImpl::NodeRef Node, unsigned Level) {
|
||||
if (Level)
|
||||
deleteBranch(&Node.get<Branch>());
|
||||
deleteNode(&Node.get<Branch>());
|
||||
else
|
||||
deleteLeaf(&Node.get<Leaf>());
|
||||
deleteNode(&Node.get<Leaf>());
|
||||
}
|
||||
|
||||
template <typename KeyT, typename ValT, unsigned N, typename Traits>
|
||||
@ -1523,11 +1522,14 @@ class IntervalMap<KeyT, ValT, N, Traits>::iterator : public const_iterator {
|
||||
bool insertNode(unsigned Level, IntervalMapImpl::NodeRef Node, KeyT Stop);
|
||||
template <typename NodeT> bool overflow(unsigned Level);
|
||||
void treeInsert(KeyT a, KeyT b, ValT y);
|
||||
|
||||
void eraseNode(unsigned Level);
|
||||
void treeErase();
|
||||
public:
|
||||
/// insert - Insert mapping [a;b] -> y before the current position.
|
||||
void insert(KeyT a, KeyT b, ValT y);
|
||||
|
||||
/// erase - Erase the current interval.
|
||||
void erase();
|
||||
};
|
||||
|
||||
/// setNodeStop - Update the stop key of the current node at level and above.
|
||||
@ -1629,8 +1631,43 @@ iterator::insert(KeyT a, KeyT b, ValT y) {
|
||||
template <typename KeyT, typename ValT, unsigned N, typename Traits>
|
||||
void IntervalMap<KeyT, ValT, N, Traits>::
|
||||
iterator::treeInsert(KeyT a, KeyT b, ValT y) {
|
||||
using namespace IntervalMapImpl;
|
||||
IntervalMap &IM = *this->map;
|
||||
IntervalMapImpl::Path &P = this->path;
|
||||
Path &P = this->path;
|
||||
|
||||
// Check if this insertion will extend the node to the left.
|
||||
if (P.valid() && P.leafOffset() == 0 &&
|
||||
Traits::startLess(a, P.leaf<Leaf>().start(0))) {
|
||||
// Node is growing to the left, will it affect a left sibling node?
|
||||
if (NodeRef Sib = P.getLeftSibling(IM.height)) {
|
||||
Leaf &SibLeaf = Sib.get<Leaf>();
|
||||
unsigned SibOfs = Sib.size() - 1;
|
||||
if (SibLeaf.value(SibOfs) == y &&
|
||||
Traits::adjacent(SibLeaf.stop(SibOfs), a)) {
|
||||
// This insertion will coalesce with the last entry in SibLeaf. We can
|
||||
// handle it in two ways:
|
||||
// 1. Extend SibLeaf.stop to b and be done, or
|
||||
// 2. Extend a to SibLeaf, erase the SibLeaf entry and continue.
|
||||
// We prefer 1., but need 2 when coalescing to the right as well.
|
||||
Leaf &CurLeaf = P.leaf<Leaf>();
|
||||
P.moveLeft(IM.height);
|
||||
if (Traits::stopLess(b, CurLeaf.start(0)) &&
|
||||
(y != CurLeaf.value(0) || !Traits::adjacent(b, CurLeaf.start(0)))) {
|
||||
// Easy, just extend SibLeaf and we're done.
|
||||
setNodeStop(IM.height, SibLeaf.stop(SibOfs) = b);
|
||||
return;
|
||||
} else {
|
||||
// We have both left and right coalescing. Erase the old SibLeaf entry
|
||||
// and continue inserting the larger interval.
|
||||
a = SibLeaf.start(SibOfs);
|
||||
erase();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No left sibling means we are at begin(). Update cached bound.
|
||||
IM.rootBranchStart() = a;
|
||||
}
|
||||
}
|
||||
|
||||
P.legalizeForInsert(IM.height);
|
||||
IdxPair IP = P.leaf<Leaf>().insertFrom(P.leafOffset(), P.leafSize(), a, b, y);
|
||||
@ -1649,8 +1686,91 @@ iterator::treeInsert(KeyT a, KeyT b, ValT y) {
|
||||
// Insert was the last node entry, update stops.
|
||||
if (IP.first == IP.second - 1)
|
||||
setNodeStop(IM.height, P.leaf<Leaf>().stop(IP.first));
|
||||
}
|
||||
|
||||
// FIXME: Handle cross-node coalescing.
|
||||
/// erase - erase the current interval and move to the next position.
|
||||
template <typename KeyT, typename ValT, unsigned N, typename Traits>
|
||||
void IntervalMap<KeyT, ValT, N, Traits>::
|
||||
iterator::erase() {
|
||||
IntervalMap &IM = *this->map;
|
||||
IntervalMapImpl::Path &P = this->path;
|
||||
assert(P.valid() && "Cannot erase end()");
|
||||
if (this->branched())
|
||||
return treeErase();
|
||||
IM.rootLeaf().erase(P.leafOffset(), IM.rootSize);
|
||||
P.setSize(0, --IM.rootSize);
|
||||
}
|
||||
|
||||
/// treeErase - erase() for a branched tree.
|
||||
template <typename KeyT, typename ValT, unsigned N, typename Traits>
|
||||
void IntervalMap<KeyT, ValT, N, Traits>::
|
||||
iterator::treeErase() {
|
||||
IntervalMap &IM = *this->map;
|
||||
IntervalMapImpl::Path &P = this->path;
|
||||
Leaf &Node = P.leaf<Leaf>();
|
||||
|
||||
// Nodes are not allowed to become empty.
|
||||
if (P.leafSize() == 1) {
|
||||
IM.deleteNode(&Node);
|
||||
eraseNode(IM.height);
|
||||
return;
|
||||
}
|
||||
|
||||
// Erase current entry.
|
||||
Node.erase(P.leafOffset(), P.leafSize());
|
||||
unsigned NewSize = P.leafSize() - 1;
|
||||
P.setSize(IM.height, NewSize);
|
||||
// When we erase the last entry, update stop and move to a legal position.
|
||||
if (P.leafOffset() == NewSize) {
|
||||
setNodeStop(IM.height, Node.stop(NewSize - 1));
|
||||
P.moveRight(IM.height);
|
||||
}
|
||||
}
|
||||
|
||||
/// eraseNode - Erase the current node at Level from its parent and move path to
|
||||
/// the first entry of the next sibling node.
|
||||
/// The node must be deallocated by the caller.
|
||||
/// @param Level 1..height, the root node cannot be erased.
|
||||
template <typename KeyT, typename ValT, unsigned N, typename Traits>
|
||||
void IntervalMap<KeyT, ValT, N, Traits>::
|
||||
iterator::eraseNode(unsigned Level) {
|
||||
assert(Level && "Cannot erase root node");
|
||||
IntervalMap &IM = *this->map;
|
||||
IntervalMapImpl::Path &P = this->path;
|
||||
|
||||
if (--Level == 0) {
|
||||
IM.rootBranch().erase(P.offset(0), IM.rootSize);
|
||||
P.setSize(0, --IM.rootSize);
|
||||
// If this cleared the root, switch to height=0.
|
||||
if (IM.empty()) {
|
||||
IM.switchRootToLeaf();
|
||||
this->setRoot(0);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Remove node ref from branch node at Level.
|
||||
Branch &Parent = P.node<Branch>(Level);
|
||||
if (P.size(Level) == 1) {
|
||||
// Branch node became empty, remove it recursively.
|
||||
IM.deleteNode(&Parent);
|
||||
eraseNode(Level);
|
||||
} else {
|
||||
// Branch node won't become empty.
|
||||
Parent.erase(P.offset(Level), P.size(Level));
|
||||
unsigned NewSize = P.size(Level) - 1;
|
||||
P.setSize(Level, NewSize);
|
||||
// If we removed the last branch, update stop and move to a legal pos.
|
||||
if (P.offset(Level) == NewSize) {
|
||||
setNodeStop(Level, Parent.stop(NewSize - 1));
|
||||
P.moveRight(Level);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Update path cache for the new right sibling position.
|
||||
if (P.valid()) {
|
||||
P.reset(Level + 1);
|
||||
P.offset(Level + 1) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// overflow - Distribute entries of the current node evenly among
|
||||
@ -1685,7 +1805,7 @@ iterator::overflow(unsigned Level) {
|
||||
// Do we have a right sibling?
|
||||
NodeRef RightSib = P.getRightSibling(Level);
|
||||
if (RightSib) {
|
||||
Offset += Elements = CurSize[Nodes] = RightSib.size();
|
||||
Elements += CurSize[Nodes] = RightSib.size();
|
||||
Node[Nodes++] = &RightSib.get<NodeT>();
|
||||
}
|
||||
|
||||
@ -1697,7 +1817,7 @@ iterator::overflow(unsigned Level) {
|
||||
CurSize[Nodes] = CurSize[NewNode];
|
||||
Node[Nodes] = Node[NewNode];
|
||||
CurSize[NewNode] = 0;
|
||||
Node[NewNode] = new(this->map->allocator.template Allocate<NodeT>())NodeT();
|
||||
Node[NewNode] = this->map->newNode<NodeT>();
|
||||
++Nodes;
|
||||
}
|
||||
|
||||
|
@ -424,4 +424,28 @@ TEST(IntervalMapTest, Branched2) {
|
||||
EXPECT_TRUE(map.begin() == map.end());
|
||||
}
|
||||
|
||||
// Random insertions, coalescing to a single interval.
|
||||
TEST(IntervalMapTest, RandomCoalescing) {
|
||||
UU4Map::Allocator allocator;
|
||||
UU4Map map(allocator);
|
||||
|
||||
// This is a poor PRNG with maximal period:
|
||||
// x_n = 5 x_{n-1} + 1 mod 2^N
|
||||
|
||||
unsigned x = 100;
|
||||
for (unsigned i = 0; i != 4096; ++i) {
|
||||
map.insert(10*x, 10*x+9, 1);
|
||||
EXPECT_GE(10*x, map.start());
|
||||
EXPECT_LE(10*x+9, map.stop());
|
||||
x = (5*x+1)%4096;
|
||||
}
|
||||
|
||||
// Map should be fully coalesced after that exercise.
|
||||
EXPECT_FALSE(map.empty());
|
||||
EXPECT_EQ(0u, map.start());
|
||||
EXPECT_EQ(40959u, map.stop());
|
||||
EXPECT_EQ(1, std::distance(map.begin(), map.end()));
|
||||
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
Loading…
Reference in New Issue
Block a user