Add IntervalMap::iterator::set{Start,Stop,Value} methods that allow limited
authorJakob Stoklund Olesen <stoklund@2pi.dk>
Fri, 3 Dec 2010 19:02:00 +0000 (19:02 +0000)
committerJakob Stoklund Olesen <stoklund@2pi.dk>
Fri, 3 Dec 2010 19:02:00 +0000 (19:02 +0000)
editing of the current interval.

These methods may cause coalescing, there are corresponding set*Unchecked
methods for editing without coalescing. The non-coalescing methods are useful
for applying monotonic transforms to all keys or values in a map without
accidentally coalescing transformed and untransformed intervals.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@120829 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/ADT/IntervalMap.h
lib/Support/IntervalMap.cpp
unittests/ADT/IntervalMapTest.cpp

index b0a0a0ba9a26a46c1b56b1bffea050beac84ad00..33569269c682d973ffac6415a26891a17ca75b58 100644 (file)
@@ -904,10 +904,10 @@ public:
     return true;
   }
 
-  /// atLastBranch - Return true if the path is at the last branch of the node
-  /// at Level.
+  /// atLastEntry - Return true if the path is at the last entry of the node at
+  /// Level.
   /// @param Level Node to examine.
-  bool atLastBranch(unsigned Level) const {
+  bool atLastEntry(unsigned Level) const {
     return path[Level].offset == path[Level].size - 1;
   }
 
@@ -1365,37 +1365,44 @@ protected:
   void treeFind(KeyT x);
   void treeAdvanceTo(KeyT x);
 
-public:
-  /// const_iterator - Create an iterator that isn't pointing anywhere.
-  const_iterator() : map(0) {}
-
-  /// valid - Return true if the current position is valid, false for end().
-  bool valid() const { return path.valid(); }
-
-  /// start - Return the beginning of the current interval.
-  const KeyT &start() const {
+  /// unsafeStart - Writable access to start() for iterator.
+  KeyT &unsafeStart() const {
     assert(valid() && "Cannot access invalid iterator");
     return branched() ? path.leaf<Leaf>().start(path.leafOffset()) :
                         path.leaf<RootLeaf>().start(path.leafOffset());
   }
 
-  /// stop - Return the end of the current interval.
-  const KeyT &stop() const {
+  /// unsafeStop - Writable access to stop() for iterator.
+  KeyT &unsafeStop() const {
     assert(valid() && "Cannot access invalid iterator");
     return branched() ? path.leaf<Leaf>().stop(path.leafOffset()) :
                         path.leaf<RootLeaf>().stop(path.leafOffset());
   }
 
-  /// value - Return the mapped value at the current interval.
-  const ValT &value() const {
+  /// unsafeValue - Writable access to value() for iterator.
+  ValT &unsafeValue() const {
     assert(valid() && "Cannot access invalid iterator");
     return branched() ? path.leaf<Leaf>().value(path.leafOffset()) :
                         path.leaf<RootLeaf>().value(path.leafOffset());
   }
 
-  const ValT &operator*() const {
-    return value();
-  }
+public:
+  /// const_iterator - Create an iterator that isn't pointing anywhere.
+  const_iterator() : map(0) {}
+
+  /// valid - Return true if the current position is valid, false for end().
+  bool valid() const { return path.valid(); }
+
+  /// start - Return the beginning of the current interval.
+  const KeyT &start() const { return unsafeStart(); }
+
+  /// stop - Return the end of the current interval.
+  const KeyT &stop() const { return unsafeStop(); }
+
+  /// value - Return the mapped value at the current interval.
+  const ValT &value() const { return unsafeValue(); }
+
+  const ValT &operator*() const { return value(); }
 
   bool operator==(const const_iterator &RHS) const {
     assert(map == RHS.map && "Cannot compare iterators from different maps");
@@ -1554,10 +1561,50 @@ class IntervalMap<KeyT, ValT, N, Traits>::iterator : public const_iterator {
   void treeInsert(KeyT a, KeyT b, ValT y);
   void eraseNode(unsigned Level);
   void treeErase(bool UpdateRoot = true);
+  bool canCoalesceLeft(KeyT Start, ValT x);
+  bool canCoalesceRight(KeyT Stop, ValT x);
+
 public:
   /// iterator - Create null iterator.
   iterator() {}
 
+  /// setStart - Move the start of the current interval.
+  /// This may cause coalescing with the previous interval.
+  /// @param a New start key, must not overlap the previous interval.
+  void setStart(KeyT a);
+
+  /// setStop - Move the end of the current interval.
+  /// This may cause coalescing with the following interval.
+  /// @param b New stop key, must not overlap the following interval.
+  void setStop(KeyT b);
+
+  /// setValue - Change the mapped value of the current interval.
+  /// This may cause coalescing with the previous and following intervals.
+  /// @param x New value.
+  void setValue(ValT x);
+
+  /// setStartUnchecked - Move the start of the current interval without
+  /// checking for coalescing or overlaps.
+  /// This should only be used when it is known that coalescing is not required.
+  /// @param a New start key.
+  void setStartUnchecked(KeyT a) { this->unsafeStart() = a; }
+
+  /// setStopUnchecked - Move the end of the current interval without checking
+  /// for coalescing or overlaps.
+  /// This should only be used when it is known that coalescing is not required.
+  /// @param b New stop key.
+  void setStopUnchecked(KeyT b) {
+    this->unsafeStop() = b;
+    // Update keys in branch nodes as well.
+    if (this->path.atLastEntry(this->path.height()))
+      setNodeStop(this->path.height(), b);
+  }
+
+  /// setValueUnchecked - Change the mapped value of the current interval
+  /// without checking for coalescing.
+  /// @param x New value.
+  void setValueUnchecked(ValT x) { this->unsafeValue() = x; }
+
   /// insert - Insert mapping [a;b] -> y before the current position.
   void insert(KeyT a, KeyT b, ValT y);
 
@@ -1588,6 +1635,62 @@ public:
 
 };
 
+/// canCoalesceLeft - Can the current interval coalesce to the left after
+/// changing start or value?
+/// @param Start New start of current interval.
+/// @param Value New value for current interval.
+/// @return True when updating the current interval would enable coalescing.
+template <typename KeyT, typename ValT, unsigned N, typename Traits>
+bool IntervalMap<KeyT, ValT, N, Traits>::
+iterator::canCoalesceLeft(KeyT Start, ValT Value) {
+  using namespace IntervalMapImpl;
+  Path &P = this->path;
+  if (!this->branched()) {
+    unsigned i = P.leafOffset();
+    RootLeaf &Node = P.leaf<RootLeaf>();
+    return i && Node.value(i-1) == Value &&
+                Traits::adjacent(Node.stop(i-1), Start);
+  }
+  // Branched.
+  if (unsigned i = P.leafOffset()) {
+    Leaf &Node = P.leaf<Leaf>();
+    return Node.value(i-1) == Value && Traits::adjacent(Node.stop(i-1), Start);
+  } else if (NodeRef NR = P.getLeftSibling(P.height())) {
+    unsigned i = NR.size() - 1;
+    Leaf &Node = NR.get<Leaf>();
+    return Node.value(i) == Value && Traits::adjacent(Node.stop(i), Start);
+  }
+  return false;
+}
+
+/// canCoalesceRight - Can the current interval coalesce to the right after
+/// changing stop or value?
+/// @param Stop New stop of current interval.
+/// @param Value New value for current interval.
+/// @return True when updating the current interval would enable coalescing.
+template <typename KeyT, typename ValT, unsigned N, typename Traits>
+bool IntervalMap<KeyT, ValT, N, Traits>::
+iterator::canCoalesceRight(KeyT Stop, ValT Value) {
+  using namespace IntervalMapImpl;
+  Path &P = this->path;
+  unsigned i = P.leafOffset() + 1;
+  if (!this->branched()) {
+    if (i >= P.leafSize())
+      return false;
+    RootLeaf &Node = P.leaf<RootLeaf>();
+    return Node.value(i) == Value && Traits::adjacent(Stop, Node.start(i));
+  }
+  // Branched.
+  if (i < P.leafSize()) {
+    Leaf &Node = P.leaf<Leaf>();
+    return Node.value(i) == Value && Traits::adjacent(Stop, Node.start(i));
+  } else if (NodeRef NR = P.getRightSibling(P.height())) {
+    Leaf &Node = NR.get<Leaf>();
+    return Node.value(0) == Value && Traits::adjacent(Stop, Node.start(0));
+  }
+  return false;
+}
+
 /// setNodeStop - Update the stop key of the current node at level and above.
 template <typename KeyT, typename ValT, unsigned N, typename Traits>
 void IntervalMap<KeyT, ValT, N, Traits>::
@@ -1599,13 +1702,61 @@ iterator::setNodeStop(unsigned Level, KeyT Stop) {
   // Update nodes pointing to the current node.
   while (--Level) {
     P.node<Branch>(Level).stop(P.offset(Level)) = Stop;
-    if (!P.atLastBranch(Level))
+    if (!P.atLastEntry(Level))
       return;
   }
   // Update root separately since it has a different layout.
   P.node<RootBranch>(Level).stop(P.offset(Level)) = Stop;
 }
 
+template <typename KeyT, typename ValT, unsigned N, typename Traits>
+void IntervalMap<KeyT, ValT, N, Traits>::
+iterator::setStart(KeyT a) {
+  assert(Traits::stopLess(a, this->stop()) && "Cannot move start beyond stop");
+  KeyT &CurStart = this->unsafeStart();
+  if (!Traits::startLess(a, CurStart) || !canCoalesceLeft(a, this->value())) {
+    CurStart = a;
+    return;
+  }
+  // Coalesce with the interval to the left.
+  --*this;
+  a = this->start();
+  erase();
+  setStartUnchecked(a);
+}
+
+template <typename KeyT, typename ValT, unsigned N, typename Traits>
+void IntervalMap<KeyT, ValT, N, Traits>::
+iterator::setStop(KeyT b) {
+  assert(Traits::stopLess(this->start(), b) && "Cannot move stop beyond start");
+  if (Traits::startLess(b, this->stop()) ||
+      !canCoalesceRight(b, this->value())) {
+    setStopUnchecked(b);
+    return;
+  }
+  // Coalesce with interval to the right.
+  KeyT a = this->start();
+  erase();
+  setStartUnchecked(a);
+}
+
+template <typename KeyT, typename ValT, unsigned N, typename Traits>
+void IntervalMap<KeyT, ValT, N, Traits>::
+iterator::setValue(ValT x) {
+  setValueUnchecked(x);
+  if (canCoalesceRight(this->stop(), x)) {
+    KeyT a = this->start();
+    erase();
+    setStartUnchecked(a);
+  }
+  if (canCoalesceLeft(this->start(), x)) {
+    --*this;
+    KeyT a = this->start();
+    erase();
+    setStartUnchecked(a);
+  }
+}
+
 /// insertNode - insert a node before the current path at level.
 /// Leave the current path pointing at the new node.
 /// @param Level path index of the node to be inserted.
@@ -1650,7 +1801,7 @@ iterator::insertNode(unsigned Level, IntervalMapImpl::NodeRef Node, KeyT Stop) {
   }
   P.node<Branch>(Level).insert(P.offset(Level), P.size(Level), Node, Stop);
   P.setSize(Level, P.size(Level) + 1);
-  if (P.atLastBranch(Level))
+  if (P.atLastEntry(Level))
     setNodeStop(Level, Stop);
   P.reset(Level + 1);
   return SplitRoot;
index 6f39b183a1c8d8eb0b1d463e8c16586ac40ef6a9..4dfcc404ca42833f6c2b397c7626aebc368db9a8 100644 (file)
@@ -79,11 +79,11 @@ NodeRef Path::getRightSibling(unsigned Level) const {
 
   // Go up the tree until we can go right.
   unsigned l = Level - 1;
-  while (l && atLastBranch(l))
+  while (l && atLastEntry(l))
     --l;
 
   // We can't go right.
-  if (atLastBranch(l))
+  if (atLastEntry(l))
     return NodeRef();
 
   // NR is the subtree containing our right sibling.
@@ -100,7 +100,7 @@ void Path::moveRight(unsigned Level) {
 
   // Go up the tree until we can go right.
   unsigned l = Level - 1;
-  while (l && atLastBranch(l))
+  while (l && atLastEntry(l))
     --l;
 
   // NR is the subtree containing our right sibling. If we hit end(), we have
index 445afcaf7a2bc91ae86a2173b4f45eab9a12217b..fc16a3279823003941f3f912ac833cd9cb8fab0b 100644 (file)
@@ -14,8 +14,7 @@ using namespace llvm;
 
 namespace {
 
-typedef IntervalMap<unsigned, unsigned> UUMap;
-typedef IntervalMap<unsigned, unsigned, 4> UU4Map;
+typedef IntervalMap<unsigned, unsigned, 4> UUMap;
 
 // Empty map tests
 TEST(IntervalMapTest, EmptyMap) {
@@ -99,6 +98,40 @@ TEST(IntervalMapTest, SingleEntryMap) {
   EXPECT_TRUE(I == map.begin());
   EXPECT_FALSE(I == map.end());
 
+  // Change the value.
+  I.setValue(2);
+  ASSERT_TRUE(I.valid());
+  EXPECT_EQ(100u, I.start());
+  EXPECT_EQ(150u, I.stop());
+  EXPECT_EQ(2u, I.value());
+
+  // Grow the bounds.
+  I.setStart(0);
+  ASSERT_TRUE(I.valid());
+  EXPECT_EQ(0u, I.start());
+  EXPECT_EQ(150u, I.stop());
+  EXPECT_EQ(2u, I.value());
+
+  I.setStop(200);
+  ASSERT_TRUE(I.valid());
+  EXPECT_EQ(0u, I.start());
+  EXPECT_EQ(200u, I.stop());
+  EXPECT_EQ(2u, I.value());
+
+  // Shrink the bounds.
+  I.setStart(150);
+  ASSERT_TRUE(I.valid());
+  EXPECT_EQ(150u, I.start());
+  EXPECT_EQ(200u, I.stop());
+  EXPECT_EQ(2u, I.value());
+
+  I.setStop(160);
+  ASSERT_TRUE(I.valid());
+  EXPECT_EQ(150u, I.start());
+  EXPECT_EQ(160u, I.stop());
+  EXPECT_EQ(2u, I.value());
+
+  // Erase last elem.
   I.erase();
   EXPECT_TRUE(map.empty());
   EXPECT_EQ(0, std::distance(map.begin(), map.end()));
@@ -160,6 +193,18 @@ TEST(IntervalMapTest, RootCoalescing) {
   EXPECT_EQ(1, std::distance(map.begin(), map.end()));
   EXPECT_EQ(90u, map.start());
   EXPECT_EQ(200u, map.stop());
+
+  // Add non-coalescing, then trigger coalescing with setValue.
+  map.insert(80, 89, 2);
+  map.insert(201, 210, 2);
+  EXPECT_EQ(3, std::distance(map.begin(), map.end()));
+  (++map.begin()).setValue(2);
+  EXPECT_EQ(1, std::distance(map.begin(), map.end()));
+  I = map.begin();
+  ASSERT_TRUE(I.valid());
+  EXPECT_EQ(80u, I.start());
+  EXPECT_EQ(210u, I.stop());
+  EXPECT_EQ(2u, I.value());
 }
 
 // Flat multi-coalescing tests.
@@ -324,12 +369,79 @@ TEST(IntervalMapTest, Branched) {
   EXPECT_EQ(20u, I.start());
   EXPECT_EQ(25u, I.stop());
 
+  // Change value, no coalescing.
+  I.setValue(0);
+  ASSERT_TRUE(I.valid());
+  EXPECT_EQ(20u, I.start());
+  EXPECT_EQ(25u, I.stop());
+  EXPECT_EQ(0u, I.value());
+
+  // Close the gap right, no coalescing.
+  I.setStop(29);
+  ASSERT_TRUE(I.valid());
+  EXPECT_EQ(20u, I.start());
+  EXPECT_EQ(29u, I.stop());
+  EXPECT_EQ(0u, I.value());
+
+  // Change value, no coalescing.
+  I.setValue(2);
+  ASSERT_TRUE(I.valid());
+  EXPECT_EQ(20u, I.start());
+  EXPECT_EQ(29u, I.stop());
+  EXPECT_EQ(2u, I.value());
+
+  // Change value, now coalescing.
+  I.setValue(3);
+  ASSERT_TRUE(I.valid());
+  EXPECT_EQ(20u, I.start());
+  EXPECT_EQ(35u, I.stop());
+  EXPECT_EQ(3u, I.value());
+
+  // Close the gap, now coalescing.
+  I.setValue(4);
+  ASSERT_TRUE(I.valid());
+  I.setStop(39);
+  ASSERT_TRUE(I.valid());
+  EXPECT_EQ(20u, I.start());
+  EXPECT_EQ(45u, I.stop());
+  EXPECT_EQ(4u, I.value());
+
   // advanceTo another node.
   I.advanceTo(200);
   ASSERT_TRUE(I.valid());
   EXPECT_EQ(200u, I.start());
   EXPECT_EQ(205u, I.stop());
 
+  // Close the gap left, no coalescing.
+  I.setStart(196);
+  ASSERT_TRUE(I.valid());
+  EXPECT_EQ(196u, I.start());
+  EXPECT_EQ(205u, I.stop());
+  EXPECT_EQ(20u, I.value());
+
+  // Change value, no coalescing.
+  I.setValue(0);
+  ASSERT_TRUE(I.valid());
+  EXPECT_EQ(196u, I.start());
+  EXPECT_EQ(205u, I.stop());
+  EXPECT_EQ(0u, I.value());
+
+  // Change value, now coalescing.
+  I.setValue(19);
+  ASSERT_TRUE(I.valid());
+  EXPECT_EQ(190u, I.start());
+  EXPECT_EQ(205u, I.stop());
+  EXPECT_EQ(19u, I.value());
+
+  // Close the gap, now coalescing.
+  I.setValue(18);
+  ASSERT_TRUE(I.valid());
+  I.setStart(186);
+  ASSERT_TRUE(I.valid());
+  EXPECT_EQ(180u, I.start());
+  EXPECT_EQ(205u, I.stop());
+  EXPECT_EQ(18u, I.value());
+
   // Erase from the front.
   I = map.begin();
   for (unsigned i = 0; i != 20; ++i) {
@@ -348,8 +460,8 @@ TEST(IntervalMapTest, Branched) {
 
 // Branched, high, non-coalescing tests.
 TEST(IntervalMapTest, Branched2) {
-  UU4Map::Allocator allocator;
-  UU4Map map(allocator);
+  UUMap::Allocator allocator;
+  UUMap map(allocator);
 
   // Insert enough intervals to force a height >= 2 tree.
   for (unsigned i = 1; i < 1000; ++i)
@@ -369,7 +481,7 @@ TEST(IntervalMapTest, Branched2) {
   }
 
   // Forward iteration.
-  UU4Map::iterator I = map.begin();
+  UUMap::iterator I = map.begin();
   for (unsigned i = 1; i < 1000; ++i) {
     ASSERT_TRUE(I.valid());
     EXPECT_EQ(10*i, I.start());
@@ -416,8 +528,8 @@ TEST(IntervalMapTest, Branched2) {
 
 // Random insertions, coalescing to a single interval.
 TEST(IntervalMapTest, RandomCoalescing) {
-  UU4Map::Allocator allocator;
-  UU4Map map(allocator);
+  UUMap::Allocator allocator;
+  UUMap map(allocator);
 
   // This is a poor PRNG with maximal period:
   // x_n = 5 x_{n-1} + 1 mod 2^N