From 6417af5daa130a40326e3bb73a5aa84fef44c7e7 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 15 Nov 2011 15:41:27 +0000 Subject: [PATCH] run_list --- run_list.h | 50 +++++++++++ run_list.tcc | 148 +++++++++++++++++++++++++++++++ unit-tests/Makefile.in | 4 + unit-tests/run_list_t.cc | 186 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 388 insertions(+) create mode 100644 run_list.h create mode 100644 run_list.tcc create mode 100644 unit-tests/run_list_t.cc diff --git a/run_list.h b/run_list.h new file mode 100644 index 0000000..5074545 --- /dev/null +++ b/run_list.h @@ -0,0 +1,50 @@ +#ifndef RUN_LIST_H +#define RUN_LIST_H + +#include + +//---------------------------------------------------------------- + +namespace base { + template + struct run { + run(T const &b, T const &e) + : b_(b), + e_(e) { + } + + bool operator< (run const &rhs) const { + return b_ < rhs.b_; + } + + T b_, e_; + }; + + template + class run_list { + public: + run_list() + : invert_(false) { + } + + void add_run(T const &b, T const &e); + void sub_run(T const &b, T const &e); + bool in_run(T const &key) const; + + void invert(); + void add(run_list const &rl); + void sub(run_list const &rl); + + private: + bool in_run_(T const &key) const; + + bool invert_; + std::set > runs_; + }; +} + +//---------------------------------------------------------------- + +#include "run_list.tcc" + +#endif diff --git a/run_list.tcc b/run_list.tcc new file mode 100644 index 0000000..212a7b0 --- /dev/null +++ b/run_list.tcc @@ -0,0 +1,148 @@ +#include // FIXME: remove + +#include + +//---------------------------------------------------------------- + +namespace { + using namespace base; + using namespace boost; + using namespace std; + + template + bool overlaps_ordered(run const &lhs, run const &rhs) { + return rhs.b_ < lhs.e_; + } + + template + bool overlaps(run const &lhs, run const &rhs) { + if (lhs.b_ <= rhs.b_) + return overlaps_ordered(lhs, rhs); + else + return overlaps_ordered(rhs, lhs); + } + + template + boost::optional > + merge_ordered_runs_if_overlapping(run const &lhs, run const &rhs) { + typedef optional > result; + + if (lhs.e_ < rhs.e_) + return result(run(lhs.b_, rhs.e_)); + + if (lhs.e_ <= rhs.e_) + return result(lhs); + + return result(); + } + + template + boost::optional > + merge_if_overlapping(run const &lhs, run const &rhs) { + if (lhs.b_ <= rhs.b_) + return merge_ordered_runs_if_overlapping(lhs, rhs); + else + return merge_ordered_runs_if_overlapping(rhs, lhs); + } + + template + pair >::const_iterator, + typename set >::const_iterator> + overlapping_range(set > const &runs, run const &r) { + // FIXME: slow, but correct implementation first + typedef typename set >::const_iterator cit; + + for (cit b = runs.begin(); b != runs.end(); ++b) { + if (overlaps(*b, r)) { + cit e = b; + ++e; + while (overlaps(*e, r)) + ++e; + + return make_pair(b, e); + } + } + + return make_pair(runs.end(), runs.end()); + } +} + +//---------------------------------------------------------------- + +template +void +run_list::add_run(T const &b, T const &e) +{ + using namespace std; + typedef typename set >::const_iterator cit; + + run r(b, e); + pair range = overlapping_range(runs_, r); + for (cit it = range.first; it != range.second; ++it) { + optional > mr = merge_if_overlapping(r, *it); + if (mr) + r = *mr; + } + + runs_.erase(range.first, range.second); + runs_.insert(r); +} + +template +void +run_list::sub_run(T const &b, T const &e) +{ + // FIXME: finish +} + +template +bool +run_list::in_run_(T const &key) const +{ + using namespace std; + + run r(key, key + 1); + typename set >::const_iterator it = runs_.lower_bound(r); + + if (it != runs_.end() && it->b_ == key) + return true; + + --it; + if (it == runs_.end()) + return false; + + return it->b_ <= key && it->e_ > key; +} + +template +bool +run_list::in_run(T const &key) const +{ + if (invert_) + return !in_run_(key); + else + return in_run_(key); +} + +template +void +run_list::invert() +{ + invert_ = !invert_; +} + +template +void +run_list::add(run_list const &rl) +{ + // FIXME: finish +} + +template +void +run_list::sub(run_list const &rl) +{ + // FIXME: finish +} + +//---------------------------------------------------------------- diff --git a/unit-tests/Makefile.in b/unit-tests/Makefile.in index f485e30..863f882 100644 --- a/unit-tests/Makefile.in +++ b/unit-tests/Makefile.in @@ -3,6 +3,7 @@ TEST_SOURCE=\ unit-tests/btree_t.cc \ unit-tests/cache_t.cc \ unit-tests/endian_t.cc \ + unit-tests/run_list_t.cc \ unit-tests/space_map_t.cc \ unit-tests/space_map_disk_t.cc \ unit-tests/transaction_manager_t.cc @@ -23,6 +24,9 @@ unit-tests/btree_t: unit-tests/btree_t.o $(OBJECTS) unit-tests/cache_t: unit-tests/cache_t.o $(OBJECTS) g++ $(CPPFLAGS) -o $@ $+ $(LIBS) +unit-tests/run_list_t: unit-tests/run_list_t.o $(OBJECTS) + g++ $(CPPFLAGS) -o $@ $+ $(LIBS) + unit-tests/space_map_t: unit-tests/space_map_t.o $(OBJECTS) g++ $(CPPFLAGS) -o $@ $+ $(LIBS) diff --git a/unit-tests/run_list_t.cc b/unit-tests/run_list_t.cc new file mode 100644 index 0000000..d9497de --- /dev/null +++ b/unit-tests/run_list_t.cc @@ -0,0 +1,186 @@ +#include "run_list.h" + +#define BOOST_TEST_MODULE RunListTests +#include + +using namespace std; +using namespace boost; +using namespace base; + +//---------------------------------------------------------------- + +namespace { + void check_run(run_list const &rl, + unsigned b, unsigned e, bool in) { + for (unsigned i = b; i < e; i++) + BOOST_CHECK(rl.in_run(i) == in); + } + + bool id(bool b) { + return b; + } + + bool negate(bool b) { + return !b; + } + + void _test_added_runs_are_recorded(bool invert) { + run_list rl; + rl.add_run(10, 87); + + if (invert) + rl.invert(); + bool (*op)(bool) = invert ? negate : id; + + check_run(rl, 0, 10, op(false)); + check_run(rl, 10, 87, op(true)); + check_run(rl, 87, 100, op(false)); + } + + void _test_multiple_runs_are_recorded(bool invert) { + run_list rl; + + rl.add_run(10, 87); + rl.add_run(100, 111); + rl.add_run(678, 789); + + if (invert) + rl.invert(); + bool (*op)(bool) = invert ? negate : id; + + check_run(rl, 0, 10, op(false)); + check_run(rl, 10, 87, op(true)); + check_run(rl, 87, 100, op(false)); + check_run(rl, 100, 111, op(true)); + check_run(rl, 111, 678, op(false)); + check_run(rl, 678, 789, op(true)); + check_run(rl, 789, 1000, op(false)); + } + + void _test_overlap_tail(bool invert) { + run_list rl; + + rl.add_run(10, 87); + rl.add_run(50, 96); + + if (invert) + rl.invert(); + bool (*op)(bool) = invert ? negate : id; + + check_run(rl, 0, 10, op(false)); + check_run(rl, 10, 96, op(true)); + check_run(rl, 96, 100, op(false)); + } + + void _test_overlap_head(bool invert) { + run_list rl; + + rl.add_run(50, 96); + rl.add_run(10, 87); + + if (invert) + rl.invert(); + bool (*op)(bool) = invert ? negate : id; + + check_run(rl, 0, 10, op(false)); + check_run(rl, 10, 96, op(true)); + check_run(rl, 96, 100, op(false)); + } + + void _test_overlap_completely(bool invert) { + run_list rl; + + rl.add_run(40, 60); + rl.add_run(10, 87); + + if (invert) + rl.invert(); + bool (*op)(bool) = invert ? negate : id; + + check_run(rl, 0, 10, op(false)); + check_run(rl, 10, 87, op(true)); + check_run(rl, 87, 100, op(false)); + } + + void _test_overlap_many(bool invert) { + run_list rl; + + rl.add_run(20, 30); + rl.add_run(35, 40); + rl.add_run(45, 50); + rl.add_run(55, 60); + rl.add_run(10, 87); + + if (invert) + rl.invert(); + bool (*op)(bool) = invert ? negate : id; + + check_run(rl, 0, 10, op(false)); + check_run(rl, 10, 87, op(true)); + check_run(rl, 87, 100, op(false)); + } +} + +//---------------------------------------------------------------- + +BOOST_AUTO_TEST_CASE(test_added_runs_are_recorded) +{ + _test_added_runs_are_recorded(false); +} + +BOOST_AUTO_TEST_CASE(test_multiple_runs_are_recorded) +{ + _test_multiple_runs_are_recorded(false); +} + +BOOST_AUTO_TEST_CASE(test_overlap_tail) +{ + _test_overlap_tail(false); +} + +BOOST_AUTO_TEST_CASE(test_overlap_head) +{ + _test_overlap_head(false); +} + +BOOST_AUTO_TEST_CASE(test_overlap_completely) +{ + _test_overlap_completely(false); +} + +BOOST_AUTO_TEST_CASE(test_overlap_many) +{ + _test_overlap_many(false); +} + +BOOST_AUTO_TEST_CASE(test_added_runs_are_recorded_inverted) +{ + _test_added_runs_are_recorded(true); +} + +BOOST_AUTO_TEST_CASE(test_multiple_runs_are_recorded_inverted) +{ + _test_multiple_runs_are_recorded(true); +} + +BOOST_AUTO_TEST_CASE(test_overlap_tail_inverted) +{ + _test_overlap_tail(true); +} + +BOOST_AUTO_TEST_CASE(test_overlap_head_inverted) +{ + _test_overlap_head(true); +} + +BOOST_AUTO_TEST_CASE(test_overlap_completely_inverted) +{ + _test_overlap_completely(true); +} + +BOOST_AUTO_TEST_CASE(test_overlap_many_inverted) +{ + _test_overlap_many(true); +} + +//----------------------------------------------------------------