From 3be837625644076cf5cb7e7e23a1e7d44f490e9a Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Mon, 8 Jul 2013 21:44:09 +0100 Subject: [PATCH] run_set --- persistent-data/run_set.h | 113 ++++++++++++++++++++++++ unit-tests/Makefile.in | 1 + unit-tests/run_set_t.cc | 179 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 293 insertions(+) create mode 100644 persistent-data/run_set.h create mode 100644 unit-tests/run_set_t.cc diff --git a/persistent-data/run_set.h b/persistent-data/run_set.h new file mode 100644 index 0000000..707dde0 --- /dev/null +++ b/persistent-data/run_set.h @@ -0,0 +1,113 @@ +#ifndef PERSISTENT_DATA_H +#define PERSISTENT_DATA_H + +#include "persistent-data/run.h" + +#include +#include + +//---------------------------------------------------------------- + +namespace base { + template + class run_set { + public: + void add(T const &b) { + add(run(b, b + 1)); + } + + void add(T const &b, T const &e) { + add(run(b, e)); + } + + void add(run const &r_) { + run r(r_); + + if (runs_.size()) { + // Correct but slow + const_iterator it = runs_.cbegin(); + + // Skip all blocks that end before r + while (it != runs_.end() && it->end_ <= r.begin_) + ++it; + + // work out which runs overlap + if (it != runs_.end()) { + r.begin_ = min_maybe(it->begin_, r.begin_); + const_iterator first = it; + while (it != runs_.end() && it->begin_ < r.end_) { + r.end_ = max_maybe(it->end_, r.end_); + ++it; + } + + // remove overlapping runs + runs_.erase(first, it); + } + } + + runs_.insert(r); + } + + void merge(run_set const &rhs) { + for (const_iterator it = rhs.begin(); it != rhs.end(); ++it) + add(*it); + } + + bool member(T const &v) const { + if (!runs_.size()) + return false; + + auto it = runs_.lower_bound(run(v)); + + if (it->begin_ == v) + return true; + + it--; + + if (it != runs_.end()) + return it->contains(v); + + return false; + } + + struct compare_begin { + bool operator ()(run const &r1, run const &r2) const { + return r1.begin_ < r2.begin_; + } + }; + + typedef std::set, compare_begin> rset; + typedef typename rset::const_iterator const_iterator; + + const_iterator begin() const { + return runs_.begin(); + } + + const_iterator end() const { + return runs_.end(); + } + + private: + typedef typename run::maybe maybe; + + static maybe min_maybe(maybe const &m1, maybe const &m2) { + if (!m1 || !m2) + return maybe(); + + return maybe(std::min(*m1, *m2)); + } + + static maybe max_maybe(maybe const &m1, maybe const &m2) { + if (!m1 || !m2) + return maybe(); + + return maybe(std::max(*m1, *m2)); + } + + std::set, compare_begin> runs_; + }; +} + +//---------------------------------------------------------------- + +#endif diff --git a/unit-tests/Makefile.in b/unit-tests/Makefile.in index 7b35855..e859514 100644 --- a/unit-tests/Makefile.in +++ b/unit-tests/Makefile.in @@ -56,6 +56,7 @@ TEST_SOURCE=\ unit-tests/damage_tracker_t.cc \ unit-tests/endian_t.cc \ unit-tests/rmap_visitor_t.cc \ + unit-tests/run_set_t.cc \ unit-tests/space_map_t.cc \ unit-tests/span_iterator_t.cc \ unit-tests/transaction_manager_t.cc diff --git a/unit-tests/run_set_t.cc b/unit-tests/run_set_t.cc new file mode 100644 index 0000000..e6f26bd --- /dev/null +++ b/unit-tests/run_set_t.cc @@ -0,0 +1,179 @@ +#include "gmock/gmock.h" + +#include "persistent-data/run_set.h" + +#include + +using namespace base; +using namespace std; +using namespace testing; + +//---------------------------------------------------------------- + +namespace { + MATCHER_P2(EqRun, b, e, "") { + return (arg.begin_ == static_cast(b)) && + (arg.end_ == static_cast(e)); + } + + class RunSetTests : public Test { + }; +} + +//---------------------------------------------------------------- + +TEST_F(RunSetTests, create) +{ + auto_ptr > rs(new run_set()); +} + +TEST_F(RunSetTests, add_single_blocks) +{ + run_set rs; + + rs.add(3u); + rs.add(8u); + rs.add(9u); +} + +TEST_F(RunSetTests, add_runs) +{ + run_set rs; + + rs.add(run(3, 8)); + rs.add(run(23, 55)); + rs.add(run(78, 190)); +} + +TEST_F(RunSetTests, member_empty_set) +{ + run_set rs; + ASSERT_THAT(rs.begin(), Eq(rs.end())); + ASSERT_FALSE(rs.member(5)); +} + +TEST_F(RunSetTests, member_many_runs) +{ + run_set rs; + + rs.add(3); + rs.add(7, 15); + rs.add(23, 28); + rs.add(25, 50); + + rs.add(101, 105); + rs.add(105, 110); + + rs.add(201, 205); + rs.add(206, 210); + + ASSERT_TRUE(rs.member(3)); + + ASSERT_FALSE(rs.member(6)); + for (unsigned i = 7; i < 15; i++) + ASSERT_TRUE(rs.member(i)); + ASSERT_FALSE(rs.member(15)); + + ASSERT_FALSE(rs.member(22)); + for (unsigned i = 23; i < 50; i++) + ASSERT_TRUE(rs.member(i)); + ASSERT_FALSE(rs.member(50)); + + ASSERT_FALSE(rs.member(100)); + for (unsigned i = 101; i < 110; i++) + ASSERT_TRUE(rs.member(i)); + ASSERT_FALSE(rs.member(110)); + + ASSERT_FALSE(rs.member(200)); + for (unsigned i = 201; i < 205; i++) + ASSERT_TRUE(rs.member(i)); + ASSERT_FALSE(rs.member(205)); + for (unsigned i = 206; i < 210; i++) + ASSERT_TRUE(rs.member(i)); + ASSERT_FALSE(rs.member(210)); +} + +TEST_F(RunSetTests, iterate_empty) +{ + run_set rs; + ASSERT_THAT(rs.begin(), Eq(rs.end())); +} + +TEST_F(RunSetTests, iterate_single) +{ + run_set rs; + rs.add(5, 10); + ASSERT_THAT(*rs.begin(), EqRun(5, 10)); + ASSERT_THAT(++rs.begin(), Eq(rs.end())); +} + +TEST_F(RunSetTests, iterate_overlapping_runs) +{ + run_set rs; + rs.add(5, 10); + rs.add(8, 15); + ASSERT_THAT(*rs.begin(), EqRun(5, 15)); + ASSERT_THAT(++rs.begin(), Eq(rs.end())); +} + +TEST_F(RunSetTests, iterate_overlapping_runs_other_way) +{ + run_set rs; + rs.add(8, 15); + rs.add(5, 10); + ASSERT_THAT(*rs.begin(), EqRun(5, 15)); + ASSERT_THAT(++rs.begin(), Eq(rs.end())); +} + +TEST_F(RunSetTests, iterate_covered_runs) +{ + run_set rs; + rs.add(8, 15); + rs.add(5, 20); + ASSERT_THAT(*rs.begin(), EqRun(5, 20)); + ASSERT_THAT(++rs.begin(), Eq(rs.end())); +} + +TEST_F(RunSetTests, iterate_covered_runs2) +{ + run_set rs; + rs.add(8, 10); + rs.add(12, 20); + rs.add(5, 30); + ASSERT_THAT(*rs.begin(), EqRun(5, 30)); + ASSERT_THAT(++rs.begin(), Eq(rs.end())); +} + +TEST_F(RunSetTests, iterate_non_overlapping_runs) +{ + run_set rs; + rs.add(5, 10); + rs.add(15, 20); + ASSERT_THAT(*rs.begin(), EqRun(5, 10)); + ASSERT_THAT(*(++rs.begin()), EqRun(15, 20)); + ASSERT_THAT(++(++(rs.begin())), Eq(rs.end())); +} + +TEST_F(RunSetTests, merge_empty_sets) +{ + run_set rs1; + run_set rs2; + + rs1.merge(rs2); + ASSERT_THAT(rs1.begin(), Eq(rs1.end())); +} + +TEST_F(RunSetTests, merge_discrete_sets) +{ + run_set rs1; + run_set rs2; + + rs1.add(5, 10); + rs2.add(15, 20); + rs1.merge(rs2); + ASSERT_THAT(*rs1.begin(), EqRun(5, 10)); + ASSERT_THAT(*(++rs1.begin()), EqRun(15, 20)); + ASSERT_THAT(++(++(rs1.begin())), Eq(rs1.end())); +} + +//----------------------------------------------------------------