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