diff --git a/src/radix.h b/src/radix.h --- a/src/radix.h +++ b/src/radix.h @@ -15,6 +15,10 @@ #include #include +template struct PassthroughAdapter { + auto &&getId(const T &e) const { return e.getId(); } +}; + /** * This is a radix tree storing values identified by a unique key. * @@ -33,20 +37,22 @@ * to speed before destroying anything. It is therefore crucial that the lock be * taken before reading anything in the tree. */ -template struct RadixTree { +template > +struct RadixTree { private: static const int BITS = 4; static const int MASK = (1 << BITS) - 1; static const size_t CHILD_PER_LEVEL = 1 << BITS; using KeyType = typename std::remove_reference().getId())>::type; + std::declval().getId(std::declval()))>::type; static const size_t KEY_BITS = 8 * sizeof(KeyType); static const uint32_t TOP_LEVEL = (KEY_BITS - 1) / BITS; struct RadixElement; struct RadixNode; + Adapter adapter; std::atomic root; public: @@ -104,9 +110,7 @@ * Insert a value into the tree. * Returns true if the value was inserted, false if it was already present. */ - bool insert(const RCUPtr &value) { - return insert(value->getId(), value); - } + bool insert(const RCUPtr &value) { return insert(getId(*value), value); } /** * Get the value corresponding to a key. @@ -124,7 +128,7 @@ } T *leaf = e.getLeaf(); - if (leaf == nullptr || leaf->getId() != key) { + if (leaf == nullptr || getId(*leaf) != key) { // We failed to find the proper element. return RCUPtr(); } @@ -184,7 +188,7 @@ SEEK_LEAF_LOOP(); T *leaf = e.getLeaf(); - if (leaf == nullptr || leaf->getId() != key) { + if (leaf == nullptr || getId(*leaf) != key) { // We failed to find the proper element. return RCUPtr(); } @@ -204,6 +208,8 @@ } private: + KeyType getId(const T &value) const { return adapter.getId(value); } + bool insert(const KeyType &key, RCUPtr value) { uint32_t level = TOP_LEVEL; @@ -228,7 +234,7 @@ } // The element was already in the tree. - const KeyType &leafKey = e.getLeaf()->getId(); + const KeyType &leafKey = getId(*e.getLeaf()); if (key == leafKey) { return false; } diff --git a/src/test/radix_tests.cpp b/src/test/radix_tests.cpp --- a/src/test/radix_tests.cpp +++ b/src/test/radix_tests.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -596,4 +597,50 @@ // clang-format on } +BOOST_AUTO_TEST_CASE(radix_adapter) { + using E = TestElement; + + struct StringKeyAdapter { + uint64_t getId(const E &e) const { + uint64_t key; + BOOST_REQUIRE(ParseUInt64(e.getId(), &key)); + return key; + } + }; + + RadixTree mytree; + + auto zero = RCUPtr::make("0"); + auto one = RCUPtr::make("1"); + auto two = RCUPtr::make("2"); + auto three = RCUPtr::make("3"); + + // Let's insert some elements + BOOST_CHECK(mytree.insert(zero)); + BOOST_CHECK(mytree.insert(one)); + BOOST_CHECK(mytree.insert(two)); + BOOST_CHECK(mytree.insert(three)); + BOOST_CHECK(!mytree.insert(zero)); + BOOST_CHECK(!mytree.insert(one)); + BOOST_CHECK(!mytree.insert(two)); + BOOST_CHECK(!mytree.insert(three)); + + // Retrieval is done using the converted key + BOOST_CHECK_EQUAL(mytree.get(0), zero); + BOOST_CHECK_EQUAL(mytree.get(1), one); + BOOST_CHECK_EQUAL(mytree.get(2), two); + BOOST_CHECK_EQUAL(mytree.get(3), three); + + // And so does removal + BOOST_CHECK(mytree.remove(0)); + BOOST_CHECK(mytree.remove(1)); + BOOST_CHECK(mytree.remove(2)); + BOOST_CHECK(mytree.remove(3)); + + BOOST_CHECK(!mytree.get(0)); + BOOST_CHECK(!mytree.get(1)); + BOOST_CHECK(!mytree.get(2)); + BOOST_CHECK(!mytree.get(3)); +} + BOOST_AUTO_TEST_SUITE_END()