diff --git a/src/ripple/basics/RangeSet.h b/src/ripple/basics/RangeSet.h index fbbaf5677b5..fa871db8b04 100644 --- a/src/ripple/basics/RangeSet.h +++ b/src/ripple/basics/RangeSet.h @@ -20,72 +20,44 @@ #ifndef RIPPLE_BASICS_RANGESET_H_INCLUDED #define RIPPLE_BASICS_RANGESET_H_INCLUDED +#include +#include #include -#include -#include - -namespace ripple { +namespace ripple +{ /** A sparse set of integers. */ -// VFALCO TODO Replace with juce::SparseSet class RangeSet { public: - static const std::uint32_t absent = static_cast (-1); + static const std::uint32_t absent = static_cast(-1); -public: - RangeSet () { } + RangeSet() = default; - bool hasValue (std::uint32_t) const; + bool hasValue(std::uint32_t) const; - std::uint32_t getFirst () const; - std::uint32_t getNext (std::uint32_t) const; - std::uint32_t getLast () const; - std::uint32_t getPrev (std::uint32_t) const; + // First number in the set + std::uint32_t getFirst() const; - // largest number not in the set that is less than the given number - std::uint32_t prevMissing (std::uint32_t) const; + // Largest number not in the set that is less than the given number + std::uint32_t prevMissing(std::uint32_t) const; // Add an item to the set - void setValue (std::uint32_t); + void setValue(std::uint32_t); // Add the closed interval to the set - void setRange (std::uint32_t, std::uint32_t); - - void clearValue (std::uint32_t); - - std::string toString () const; - - /** Returns the sum of the Lebesgue measures of all sub-ranges. */ - std::size_t - lebesgue_sum() const; + void setRange(std::uint32_t, std::uint32_t); - /** Check invariants of the data. + // Remove the item from the set + void clearValue(std::uint32_t); - This is for diagnostics, and does nothing in release builds. - */ - void checkInternalConsistency () const noexcept; + std::string toString() const; private: - void simplify (); - -private: - using Map = std::map ; - - using const_iterator = Map::const_iterator; - using const_reverse_iterator = Map::const_reverse_iterator; - using value_type = Map::value_type; - using iterator = Map::iterator; - - static bool contains (value_type const& it, std::uint32_t v) - { - return (it.first <= v) && (it.second >= v); - } - - // First is lowest value in range, last is highest value in range + using Interval = boost::icl::closed_interval; + using Map = boost::icl::interval_set; Map mRanges; }; -} // ripple - +} // namespace ripple #endif diff --git a/src/ripple/basics/impl/RangeSet.cpp b/src/ripple/basics/impl/RangeSet.cpp index ed13d5349e4..8a611eb7a71 100644 --- a/src/ripple/basics/impl/RangeSet.cpp +++ b/src/ripple/basics/impl/RangeSet.cpp @@ -18,253 +18,81 @@ //============================================================================== #include -#include #include #include -#include -#include -#include -namespace ripple { - -// VFALCO NOTE std::min and std::max not good enough? -// NOTE Why isn't this written as a template? -// TODO Replace this with std calls. -// -inline std::uint32_t min (std::uint32_t x, std::uint32_t y) +namespace ripple { - return (x < y) ? x : y; -} -inline std::uint32_t max (std::uint32_t x, std::uint32_t y) +bool +RangeSet::hasValue(std::uint32_t v) const { - return (x > y) ? x : y; + return boost::icl::contains(mRanges, v); } -bool RangeSet::hasValue (std::uint32_t v) const +std::uint32_t +RangeSet::getFirst() const { - for (auto const& it : mRanges) - { - if (contains (it, v)) - return true; - } - return false; -} - -std::uint32_t RangeSet::getFirst () const -{ - const_iterator it = mRanges.begin (); - - if (it == mRanges.end ()) + if (mRanges.empty()) return absent; - - return it->first; -} - -std::uint32_t RangeSet::getNext (std::uint32_t v) const -{ - for (auto const& it : mRanges) - { - if (it.first > v) - return it.first; - - if (contains (it, v + 1)) - return v + 1; - } - return absent; + return boost::icl::first(mRanges); } -std::uint32_t RangeSet::getLast () const +// largest number not in the set that is less than the given number +std::uint32_t +RangeSet::prevMissing(std::uint32_t v) const { - const_reverse_iterator it = mRanges.rbegin (); - - if (it == mRanges.rend ()) + if (v == 0) return absent; - - return it->second; -} - -std::uint32_t RangeSet::getPrev (std::uint32_t v) const -{ - BOOST_REVERSE_FOREACH (const value_type & it, mRanges) - { - if (it.second < v) - return it.second; - - if (contains (it, v + 1)) - return v - 1; - } - return absent; -} - -// Return the largest number not in the set that is less than the given number -// -std::uint32_t RangeSet::prevMissing (std::uint32_t v) const -{ - std::uint32_t result = absent; - - if (v != 0) - { - checkInternalConsistency (); - - // Handle the case where the loop reaches the terminating condition - // - result = v - 1; - - for (const_reverse_iterator cur = mRanges.rbegin (); cur != mRanges.rend (); ++cur) - { - // See if v-1 is in the range - if (contains (*cur, result)) - { - result = cur->first - 1; - break; - } - } - } - - assert (result == absent || !hasValue (result)); - - return result; + // Find returns an iterator pointing to the interval containing v-1 + auto it = mRanges.find(v - 1); + if (it != mRanges.end()) + return it->first() - 1; + // If no interval overlaps, then v-1 is the largest number less than v + // not in the set + return v - 1; } -void RangeSet::setValue (std::uint32_t v) +// Add an item to the set +void +RangeSet::setValue(std::uint32_t v) { - if (!hasValue (v)) - { - mRanges[v] = v; - - simplify (); - } + mRanges.insert(Interval(v)); } -void RangeSet::setRange (std::uint32_t minV, std::uint32_t maxV) +// Add the closed interval to the set +void +RangeSet::setRange(std::uint32_t minv, std::uint32_t maxv) { - while (hasValue (minV)) - { - ++minV; - - if (minV >= maxV) - return; - } - - mRanges[minV] = maxV; - - simplify (); + mRanges.insert(Interval(minv, maxv)); } -void RangeSet::clearValue (std::uint32_t v) +// Remove the item from the set +void +RangeSet::clearValue(std::uint32_t v) { - for (iterator it = mRanges.begin (); it != mRanges.end (); ++it) - { - if (contains (*it, v)) - { - if (it->first == v) - { - if (it->second == v) - { - mRanges.erase (it); - } - else - { - std::uint32_t oldEnd = it->second; - mRanges.erase(it); - mRanges[v + 1] = oldEnd; - } - } - else if (it->second == v) - { - -- (it->second); - } - else - { - std::uint32_t oldEnd = it->second; - it->second = v - 1; - mRanges[v + 1] = oldEnd; - } - - checkInternalConsistency(); - return; - } - } + mRanges.erase(Interval(v)); } -std::string RangeSet::toString () const +std::string +RangeSet::toString() const { std::string ret; for (auto const& it : mRanges) { - if (!ret.empty ()) + if (!ret.empty()) ret += ","; - if (it.first == it.second) - ret += beast::lexicalCastThrow ((it.first)); + if (it.first() == it.last()) + ret += beast::lexicalCastThrow((it.first())); else - ret += beast::lexicalCastThrow (it.first) + "-" - + beast::lexicalCastThrow (it.second); + ret += beast::lexicalCastThrow(it.first()) + "-" + + beast::lexicalCastThrow(it.last()); } - if (ret.empty ()) + if (ret.empty()) return "empty"; return ret; } -void RangeSet::simplify () -{ - iterator it = mRanges.begin (); - - while (1) - { - iterator nit = it; - - if (++nit == mRanges.end ()) - { - checkInternalConsistency(); - return; - } - - if (it->second >= (nit->first - 1)) - { - // ranges overlap - it->second = std::max(it->second, nit->second); - mRanges.erase (nit); - } - else - { - it = nit; - } - } -} - -std::size_t -RangeSet::lebesgue_sum() const -{ - std::size_t sum = mRanges.size(); - for (auto const& e : mRanges) - sum += e.second - e.first; - return sum; -} - -void RangeSet::checkInternalConsistency () const noexcept -{ -#ifndef NDEBUG - if (mRanges.size () > 1) - { - const_iterator const last = std::prev (mRanges.end ()); - - for (const_iterator cur = mRanges.begin (); cur != last; ++cur) - { - const_iterator const next = std::next (cur); - assert (cur->first <= cur->second); - assert (next->first <= next->second); - assert (cur->second + 1 < next->first); - } - } - else if (mRanges.size () == 1) - { - const_iterator const iter = mRanges.begin (); - assert (iter->first <= iter->second); - } -#endif -} - -} // ripple +} // ripple diff --git a/src/test/basics/RangeSet_test.cpp b/src/test/basics/RangeSet_test.cpp index e433a49a32f..72d14bc5529 100644 --- a/src/test/basics/RangeSet_test.cpp +++ b/src/test/basics/RangeSet_test.cpp @@ -26,69 +26,175 @@ namespace ripple { class RangeSet_test : public beast::unit_test::suite { public: - RangeSet createPredefinedSet () + void + testSetAndHas() { - RangeSet set; + testcase("set and has"); - // Set will include: - // [ 0, 5] - // [10,15] - // [20,25] - // etc... + RangeSet r1, r2; - for (int i = 0; i < 10; ++i) - set.setRange (10 * i, 10 * i + 5); + BEAST_EXPECT(r1.getFirst() == RangeSet::absent); + r1.setRange(1, 10); + r1.clearValue(5); + r1.setRange(11, 20); - return set; - } + for (int i = 1; i <= 20; ++i) + { + if (i != 5) + BEAST_EXPECT(r1.hasValue(i)); + else + BEAST_EXPECT(!r1.hasValue(i)); + } + BEAST_EXPECT(r1.getFirst() == 1); + r1.clearValue(1); + BEAST_EXPECT(r1.getFirst() == 2); + + // Create with gap at 5 + r2.setRange(1, 4); + r2.setRange(6, 10); + // Marge with existing range + r2.setRange(10, 20); + // subset of existing range + r2.setRange(11, 13); + // Extend existing range + r2.setValue(21); + for (int i = 1; i <= 21; ++i) + { + if (i != 5) + BEAST_EXPECT(r2.hasValue(i)); + else + BEAST_EXPECT(!r2.hasValue(i)); + } - void testMembership () - { - testcase ("membership"); + // Adding 5 creates complete range + r2.setValue(5); + for (int i = 1; i <= 21; ++i) + { + BEAST_EXPECT(r2.hasValue(i)); + } - RangeSet r1, r2; + // Additional tests to complete coverage + + // Isolate 1 + r2.clearValue(2); + for (int i = 1; i <= 21; ++i) + { + if (i != 2) + BEAST_EXPECT(r2.hasValue(i)); + else + BEAST_EXPECT(!r2.hasValue(i)); + } + // Remove 1 as well and shrink 21 + r2.clearValue(1); + r2.clearValue(21); + for (int i = 1; i <= 21; ++i) + { + if (i > 2 && i < 21) + BEAST_EXPECT(r2.hasValue(i)); + else + BEAST_EXPECT(!r2.hasValue(i)); + } - r1.setRange (1, 10); - r1.clearValue (5); - r1.setRange (11, 20); - r2.setRange (1, 4); - r2.setRange (6, 10); - r2.setRange (10, 20); + } - BEAST_EXPECT(!r1.hasValue (5)); + void + testOverlappingRanges() + { + RangeSet r; - BEAST_EXPECT(r2.hasValue (9)); + r.setRange(4,9); + for (int i = 1; i <= 10; ++i) + { + if (i >=4 && i <= 9) + BEAST_EXPECT(r.hasValue(i)); + else + BEAST_EXPECT(!r.hasValue(i)); + } + + // Add overlap range with front end + r.setRange(1,5); + for (int i = 1; i <= 10; ++i) + { + if (i <= 9) + BEAST_EXPECT(r.hasValue(i)); + else + BEAST_EXPECT(!r.hasValue(i)); + } + + // Add overlap with end + r.setRange(7,10); + for (int i = 1; i <= 10; ++i) + { + if(!BEAST_EXPECT(r.hasValue(i))) + std::cout << i << "\n"; + } + + // Add existing range in middle + r.setRange(5,7); + for (int i = 1; i <= 10; ++i) + { + BEAST_EXPECT(r.hasValue(i)); + } } - void testPrevMissing () + void + testPrevMissing() { - testcase ("prevMissing"); + testcase("prevMissing"); + + // Set will include: + // [ 0, 5] + // [10,15] + // [20,25] + // etc... - RangeSet const set = createPredefinedSet (); + RangeSet set; + for (int i = 0; i < 10; ++i) + set.setRange(10 * i, 10 * i + 5); for (int i = 0; i < 100; ++i) { - int const oneBelowRange = (10*(i/10))-1; + int const oneBelowRange = (10 * (i / 10)) - 1; int const expectedPrevMissing = - ((i % 10) > 6) ? (i-1) : oneBelowRange; + ((i % 10) > 6) ? (i - 1) : oneBelowRange; - BEAST_EXPECT(set.prevMissing (i) == expectedPrevMissing); + BEAST_EXPECT(set.prevMissing(i) == expectedPrevMissing); } } - void run () + void + testToString() { - testMembership (); + testcase("toString"); + + RangeSet set; + BEAST_EXPECT(set.toString() == "empty"); + + set.setValue(1); + BEAST_EXPECT(set.toString() == "1"); + + set.setRange(4, 6); + BEAST_EXPECT(set.toString() == "1,4-6"); - testPrevMissing (); + set.setValue(2); + BEAST_EXPECT(set.toString() == "1-2,4-6"); - // TODO: Traverse functions must be tested + set.clearValue(4); + set.clearValue(5); + BEAST_EXPECT(set.toString() == "1-2,6"); + } + void + run() + { + testSetAndHas(); + testOverlappingRanges(); + testPrevMissing(); + testToString(); } }; -BEAST_DEFINE_TESTSUITE(RangeSet,ripple_basics,ripple); - -} // ripple +BEAST_DEFINE_TESTSUITE(RangeSet, ripple_basics, ripple); +} // namespace ripple