From c6f16354b93234aa8df052e5c6d215e03936f21b Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Sat, 27 Nov 2010 21:12:36 +0000 Subject: [PATCH] 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 --- include/llvm/ADT/IntervalMap.h | 172 +++++++++++++++++++++++++----- unittests/ADT/IntervalMapTest.cpp | 24 +++++ 2 files changed, 170 insertions(+), 26 deletions(-) diff --git a/include/llvm/ADT/IntervalMap.h b/include/llvm/ADT/IntervalMap.h index 8eb5487b0e4..e1438a3b623 100644 --- a/include/llvm/ADT/IntervalMap.h +++ b/include/llvm/ADT/IntervalMap.h @@ -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(); - } - void deleteLeaf(Leaf *P) { - P->~Leaf(); - allocator.Deallocate(P); + template NodeT *newNode() { + return new(allocator.template Allocate()) NodeT(); } - Branch *allocBranch() { - return new(allocator.template Allocate()) Branch(); - } - void deleteBranch(Branch *P) { - P->~Branch(); + template 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().copy(rootLeaf(), pos, 0, size[n]); + Leaf *L = newNode(); + 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().copy(rootBranch(), Pos, 0, Size[n]); + Branch *B = newNode(); + B->copy(rootBranch(), Pos, 0, Size[n]); + Node[n] = NodeRef(B, Size[n]); Pos += Size[n]; } @@ -1303,9 +1302,9 @@ template void IntervalMap:: deleteNode(IntervalMapImpl::NodeRef Node, unsigned Level) { if (Level) - deleteBranch(&Node.get()); + deleteNode(&Node.get()); else - deleteLeaf(&Node.get()); + deleteNode(&Node.get()); } template @@ -1523,11 +1522,14 @@ class IntervalMap::iterator : public const_iterator { bool insertNode(unsigned Level, IntervalMapImpl::NodeRef Node, KeyT Stop); template 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 void IntervalMap:: 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().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(); + 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(); + 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().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().stop(IP.first)); +} - // FIXME: Handle cross-node coalescing. +/// erase - erase the current interval and move to the next position. +template +void IntervalMap:: +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 +void IntervalMap:: +iterator::treeErase() { + IntervalMap &IM = *this->map; + IntervalMapImpl::Path &P = this->path; + Leaf &Node = P.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 +void IntervalMap:: +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(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(); } @@ -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(); + Node[NewNode] = this->map->newNode(); ++Nodes; } diff --git a/unittests/ADT/IntervalMapTest.cpp b/unittests/ADT/IntervalMapTest.cpp index 36476a4cad9..c065362135e 100644 --- a/unittests/ADT/IntervalMapTest.cpp +++ b/unittests/ADT/IntervalMapTest.cpp @@ -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