diff --git a/libdevcore/RangeMask.h b/libdevcore/RangeMask.h index 7c402fc98..d87b81a3a 100644 --- a/libdevcore/RangeMask.h +++ b/libdevcore/RangeMask.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -35,6 +36,12 @@ class RLPStream; using UnsignedRange = std::pair; using UnsignedRanges = std::vector; +/** + * Set of elements of a certain "ground range" representable by unions of ranges inside this + * ground range. + * Ranges are given as pairs (begin, end), denoting the interval [begin, end), i.e. end is excluded. + * Supports set-theoretic operators, size and iteration. + */ template class RangeMask { @@ -44,14 +51,19 @@ public: using Range = std::pair; using Ranges = std::vector; + /// Constructs an empty range mask with empty ground range. RangeMask(): m_all(0, 0) {} + /// Constructs an empty range mask with ground range [_begin, _end). RangeMask(T _begin, T _end): m_all(_begin, _end) {} + /// Constructs an empty range mask with ground range _c. RangeMask(Range const& _c): m_all(_c) {} + /// @returns the union with the range mask _m, taking also the union of the ground ranges. RangeMask unionedWith(RangeMask const& _m) const { return operator+(_m); } RangeMask operator+(RangeMask const& _m) const { return RangeMask(*this) += _m; } - RangeMask lowest(T _items) const + /// @returns a new range mask containing the smallest _items elements (not ranges). + RangeMask lowest(decltype(T{} - T{}) _items) const { RangeMask ret(m_all); for (auto i = m_ranges.begin(); i != m_ranges.end() && _items; ++i) @@ -59,8 +71,10 @@ public: return ret; } + /// @returns the complement of the range mask relative to the ground range. RangeMask operator~() const { return inverted(); } + /// @returns a copy of this range mask representing the complement relative to the ground range. RangeMask inverted() const { RangeMask ret(m_all); @@ -76,6 +90,8 @@ public: return ret; } + /// Changes the range mask to its complement relative to the ground range and returns a + /// reference to itself. RangeMask& invert() { return *this = inverted(); } template RangeMask operator-(S const& _m) const { auto ret = *this; return ret -= _m; } @@ -92,61 +108,13 @@ public: return *this; } RangeMask& operator+=(Range const& _m) { return unionWith(_m); } - RangeMask& unionWith(Range const& _m) - { - for (auto i = _m.first; i < _m.second;) - { - assert(i >= m_all.first); - assert(i < m_all.second); - // for each number, we find the element equal or next lower. this, if any, must contain the value. - auto uit = m_ranges.upper_bound(i); - auto it = uit == m_ranges.begin() ? m_ranges.end() : std::prev(uit); - if (it == m_ranges.end() || it->second < i) - // lower range is too low to merge. - // if the next higher range is too high. - if (uit == m_ranges.end() || uit->first > _m.second) - { - // just create a new range - m_ranges[i] = _m.second; - break; - } - else - { - if (uit->first == i) - // move i to end of range - i = uit->second; - else - { - // merge with the next higher range - // move i to end of range - i = m_ranges[i] = uit->second; - i = uit->second; - m_ranges.erase(uit); - } - } - else if (it->second == i) - { - // if the next higher range is too high. - if (uit == m_ranges.end() || uit->first > _m.second) - { - // merge with the next lower range - m_ranges[it->first] = _m.second; - break; - } - else - { - // merge with both next lower & next higher. - i = m_ranges[it->first] = uit->second; - m_ranges.erase(uit); - } - } - else - i = it->second; - } - return *this; - } + /// Modifies this range mask to also include the range _m, which has to be a subset of + /// the ground range. + RangeMask& unionWith(Range const& _m); + /// Adds the single element _i to the range mask. RangeMask& operator+=(T _m) { return unionWith(_m); } + /// Adds the single element _i to the range mask. RangeMask& unionWith(T _i) { return operator+=(Range(_i, _i + 1)); @@ -181,10 +149,12 @@ public: m_all = std::make_pair(0, 0); } + /// @returns the ground range. std::pair const& all() const { return m_all; } + /// Extends the ground range to include _i. void extendAll(T _i) { m_all = std::make_pair(std::min(m_all.first, _i), std::max(m_all.second, _i + 1)); } - class const_iterator + class const_iterator: public std::iterator { friend class RangeMask; @@ -208,6 +178,8 @@ public: const_iterator begin() const { return const_iterator(*this, false); } const_iterator end() const { return const_iterator(*this, true); } + /// @returns the smallest element in the range mask that is larger than _t or the end of the + /// base range if such an element does not exist. T next(T _t) const { _t++; @@ -219,6 +191,7 @@ public: return uit == m_ranges.end() ? m_all.second : uit->first; } + /// @returns the number of elements (not ranges) in the range mask. size_t size() const { size_t c = 0; @@ -228,7 +201,9 @@ public: } private: + /// The ground range. UnsignedRange m_all; + /// Mapping begin -> end containing the ranges. std::map m_ranges; }; @@ -241,4 +216,65 @@ template inline std::ostream& operator<<(std::ostream& _out, RangeMask return _out; } +template +RangeMask& RangeMask::unionWith(RangeMask::Range const& _m) +{ + for (auto i = _m.first; i < _m.second;) + { + assert(i >= m_all.first); + assert(i < m_all.second); + // For each number, we find the element equal or next lower. this, if any, must contain the value. + // First range that starts after i. + auto rangeAfter = m_ranges.upper_bound(i); + // Range before rangeAfter or "end" if the rangeAfter is the first ever... + auto it = rangeAfter == m_ranges.begin() ? m_ranges.end() : std::prev(rangeAfter); + if (it == m_ranges.end() || it->second < i) + { + // i is either before the first range or between two ranges (with some distance + // so that we cannot merge it onto "it"). + // lower range is too low to merge. + // if the next higher range is too high. + if (rangeAfter == m_ranges.end() || rangeAfter->first > _m.second) + { + // just create a new range + m_ranges[i] = _m.second; + break; + } + else + { + if (rangeAfter->first == i) + // move i to end of range + i = rangeAfter->second; + else + { + // merge with the next higher range + // move i to end of range + i = m_ranges[i] = rangeAfter->second; + m_ranges.erase(rangeAfter); + } + } + } + else if (it->second == i) + { + // The range before i ends with i. + // if the next higher range is too high. + if (rangeAfter == m_ranges.end() || rangeAfter->first > _m.second) + { + // merge with the next lower range + m_ranges[it->first] = _m.second; + break; + } + else + { + // merge with both next lower & next higher. + i = m_ranges[it->first] = rangeAfter->second; + m_ranges.erase(rangeAfter); + } + } + else + i = it->second; + } + return *this; +} + } diff --git a/libethereum/BlockChainSync.h b/libethereum/BlockChainSync.h index 449dd49d1..793b05b5b 100644 --- a/libethereum/BlockChainSync.h +++ b/libethereum/BlockChainSync.h @@ -24,7 +24,6 @@ #include #include -#include #include #include #include "CommonNet.h" diff --git a/libethereum/EthereumHost.h b/libethereum/EthereumHost.h index 182c3d4cd..a4b9d0006 100644 --- a/libethereum/EthereumHost.h +++ b/libethereum/EthereumHost.h @@ -31,7 +31,6 @@ #include #include -#include #include #include #include "CommonNet.h" diff --git a/libethereum/EthereumPeer.h b/libethereum/EthereumPeer.h index d506ffb3f..e8842c8af 100644 --- a/libethereum/EthereumPeer.h +++ b/libethereum/EthereumPeer.h @@ -31,7 +31,6 @@ #include #include -#include #include #include #include "CommonNet.h" diff --git a/libp2p/Host.h b/libp2p/Host.h index 9523d0cca..b7ebb3951 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -33,7 +33,6 @@ #include #include -#include #include #include #include "NodeTable.h" diff --git a/libp2p/Session.h b/libp2p/Session.h index 0a55b2653..8d8c3ea1e 100644 --- a/libp2p/Session.h +++ b/libp2p/Session.h @@ -31,7 +31,6 @@ #include #include -#include #include #include "RLPXFrameCoder.h" #include "RLPXSocket.h" diff --git a/test/libdevcore/RangeMask.cpp b/test/libdevcore/RangeMask.cpp new file mode 100644 index 000000000..05f58963a --- /dev/null +++ b/test/libdevcore/RangeMask.cpp @@ -0,0 +1,148 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file RangeMask.cpp + * @author Christian + * @date 2015 + */ + +#include +#include "../TestHelper.h" + +using namespace std; +using namespace dev; + +namespace dev +{ +namespace test +{ + +BOOST_AUTO_TEST_SUITE(RangeMaskTest) + +BOOST_AUTO_TEST_CASE(constructor) +{ + using RM = RangeMask; + using Range = pair; + for (RM r: {RM(), RM(1, 10), RM(Range(2, 10))}) + { + BOOST_CHECK(r.empty()); + BOOST_CHECK(!r.contains(0)); + BOOST_CHECK(!r.contains(1)); + BOOST_CHECK_EQUAL(0, r.size()); + } + BOOST_CHECK(RM().full()); + BOOST_CHECK(!RM(1, 10).full()); + BOOST_CHECK(!RM(Range(2, 10)).full()); +} + +BOOST_AUTO_TEST_CASE(simple_unions) +{ + using RM = RangeMask; + using Range = pair; + RM m(Range(0, 2000)); + m.unionWith(Range(1, 2)); + BOOST_CHECK_EQUAL(m.size(), 1); + m.unionWith(Range(50, 250)); + BOOST_CHECK_EQUAL(m.size(), 201); + m.unionWith(Range(10, 16)); + BOOST_CHECK_EQUAL(m.size(), 207); + BOOST_CHECK(m.contains(1)); + BOOST_CHECK(m.contains(11)); + BOOST_CHECK(m.contains(51)); + BOOST_CHECK(m.contains(200)); + BOOST_CHECK(!m.contains(2)); + BOOST_CHECK(!m.contains(7)); + BOOST_CHECK(!m.contains(17)); + BOOST_CHECK(!m.contains(258)); +} + +BOOST_AUTO_TEST_CASE(empty_union) +{ + using RM = RangeMask; + using Range = pair; + RM m(Range(0, 2000)); + m.unionWith(Range(3, 6)); + BOOST_CHECK_EQUAL(m.size(), 3); + m.unionWith(Range(50, 50)); + BOOST_CHECK_EQUAL(m.size(), 3); + m.unionWith(Range(0, 0)); + BOOST_CHECK_EQUAL(m.size(), 3); + m.unionWith(Range(1, 1)); + BOOST_CHECK_EQUAL(m.size(), 3); + m.unionWith(Range(2, 2)); + BOOST_CHECK_EQUAL(m.size(), 3); + m.unionWith(Range(3, 3)); + BOOST_CHECK_EQUAL(m.size(), 3); +} + +BOOST_AUTO_TEST_CASE(overlapping_unions) +{ + using RM = RangeMask; + using Range = pair; + RM m(Range(0, 2000)); + m.unionWith(Range(10, 20)); + BOOST_CHECK_EQUAL(10, m.size()); + m.unionWith(Range(30, 40)); + BOOST_CHECK_EQUAL(20, m.size()); + m.unionWith(Range(15, 30)); + BOOST_CHECK_EQUAL(40 - 10, m.size()); + m.unionWith(Range(50, 60)); + m.unionWith(Range(45, 55)); + // [40, 45) still missing here + BOOST_CHECK_EQUAL(60 - 10 - 5, m.size()); + m.unionWith(Range(15, 56)); + BOOST_CHECK_EQUAL(60 - 10, m.size()); + m.unionWith(Range(15, 65)); + BOOST_CHECK_EQUAL(65 - 10, m.size()); + m.unionWith(Range(5, 70)); + BOOST_CHECK_EQUAL(70 - 5, m.size()); +} + +BOOST_AUTO_TEST_CASE(complement) +{ + using RM = RangeMask; + using Range = pair; + RM m(Range(0, 2000)); + m.unionWith(7).unionWith(9); + m = ~m; + m.unionWith(7).unionWith(9); + m = ~m; + BOOST_CHECK(m.empty()); + + m += Range(0, 10); + m += Range(1000, 2000); + m.invert(); + BOOST_CHECK_EQUAL(m.size(), 1000 - 10); +} + +BOOST_AUTO_TEST_CASE(iterator) +{ + using RM = RangeMask; + using Range = pair; + RM m(Range(0, 2000)); + m.unionWith(Range(7, 9)); + m.unionWith(11); + m.unionWith(Range(200, 205)); + + vector elements; + copy(m.begin(), m.end(), back_inserter(elements)); + BOOST_CHECK(elements == (vector{7, 8, 11, 200, 201, 202, 203, 204})); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +}