diff --git a/src/radix.h b/src/radix.h --- a/src/radix.h +++ b/src/radix.h @@ -90,6 +90,46 @@ return const_cast(this)->get(key); } + /** + * Remove an element from the tree. + * Returns true if the element was removed from the tree, false if the + * element wasn't found in the tree. + */ + bool remove(const K &key) { + uint32_t level = TOP_LEVEL; + + RCULock lock; + std::atomic *eptr = &root; + + RadixElement e = eptr->load(); + + // Walk down the tree until we find a leaf for our node. + while (e.isNode()) { + Node: + eptr = e.getNode()->get(level--, key); + e = eptr->load(); + } + + T *leaf = e.getLeaf(); + if (leaf == nullptr || leaf->getId() != key) { + // We failed to find the proper element. + return false; + } + + // We have the proper element, try to delete it. + if (eptr->compare_exchange_strong(e, RadixElement())) { + return true; + } + + // The element was replaced, either by a subtree or another element. + if (e.isNode()) { + goto Node; + } + + // The element in the slot is not the one we are looking for. + return false; + } + private: bool insert(const K &key, T *value) { uint32_t level = TOP_LEVEL; 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 @@ -152,6 +152,94 @@ testGet(); } +template void testRemove() { + typedef TestElement E; + RadixTree mytree; + + E zero(0); + E one(1); + E two(2); + E three(3); + + // Removing an element that isn't in the tree returns false. + BOOST_CHECK(!mytree.remove(1)); + + // Insert an element into the tree and check you can remove it. + BOOST_CHECK(mytree.insert(&one)); + BOOST_CHECK(mytree.remove(1)); + BOOST_CHECK_EQUAL(mytree.get(1), nullptr); + + // Insert several elements and remove them. + BOOST_CHECK(mytree.insert(&zero)); + BOOST_CHECK(mytree.insert(&one)); + BOOST_CHECK(mytree.insert(&two)); + BOOST_CHECK(mytree.insert(&three)); + + BOOST_CHECK(mytree.remove(0)); + BOOST_CHECK(mytree.remove(1)); + BOOST_CHECK(mytree.remove(2)); + BOOST_CHECK(mytree.remove(3)); + + BOOST_CHECK_EQUAL(mytree.get(0), nullptr); + BOOST_CHECK_EQUAL(mytree.get(1), nullptr); + BOOST_CHECK_EQUAL(mytree.get(2), nullptr); + BOOST_CHECK_EQUAL(mytree.get(3), nullptr); + + // Once the elements are removed, removing them again returns false. + BOOST_CHECK(!mytree.remove(0)); + BOOST_CHECK(!mytree.remove(1)); + BOOST_CHECK(!mytree.remove(2)); + BOOST_CHECK(!mytree.remove(3)); + + // Check extreme values. + typedef typename std::make_signed::type SK; + K maxsk = std::numeric_limits::max(); + K minsk = std::numeric_limits::min(); + typedef typename std::make_unsigned::type UK; + K maxuk = std::numeric_limits::max(); + + E maxsigned(maxsk); + E minsigned(minsk); + E minusone(maxuk); + E minustwo(maxuk - 1); + + // Insert them all. + BOOST_CHECK(mytree.insert(&zero)); + BOOST_CHECK(mytree.insert(&one)); + BOOST_CHECK(mytree.insert(&two)); + BOOST_CHECK(mytree.insert(&three)); + BOOST_CHECK(mytree.insert(&maxsigned)); + BOOST_CHECK(mytree.insert(&minsigned)); + BOOST_CHECK(mytree.insert(&minusone)); + BOOST_CHECK(mytree.insert(&minustwo)); + + // Delete them all + BOOST_CHECK(mytree.remove(0)); + BOOST_CHECK(mytree.remove(1)); + BOOST_CHECK(mytree.remove(2)); + BOOST_CHECK(mytree.remove(3)); + BOOST_CHECK(mytree.remove(maxsk)); + BOOST_CHECK(mytree.remove(minsk)); + BOOST_CHECK(mytree.remove(maxuk)); + BOOST_CHECK(mytree.remove(maxuk - 1)); + + BOOST_CHECK_EQUAL(mytree.get(0), nullptr); + BOOST_CHECK_EQUAL(mytree.get(1), nullptr); + BOOST_CHECK_EQUAL(mytree.get(2), nullptr); + BOOST_CHECK_EQUAL(mytree.get(3), nullptr); + BOOST_CHECK_EQUAL(mytree.get(maxsk), nullptr); + BOOST_CHECK_EQUAL(mytree.get(minsk), nullptr); + BOOST_CHECK_EQUAL(mytree.get(maxuk), nullptr); + BOOST_CHECK_EQUAL(mytree.get(maxuk - 1), nullptr); +} + +BOOST_AUTO_TEST_CASE(remove_test) { + testRemove(); + testRemove(); + testRemove(); + testRemove(); +} + #define THREADS 128 #define ELEMENTS 65536