diff --git a/src/radix.h b/src/radix.h --- a/src/radix.h +++ b/src/radix.h @@ -137,6 +137,11 @@ return RCUPtr::acquire(ptr); } + template void forEachLeaf(Callable &&func) const { + RCULock lock; + forEachLeaf(root.load(), std::move(func)); + } + #define SEEK_LEAF_LOOP() \ RadixElement e = eptr->load(); \ \ @@ -243,6 +248,20 @@ #undef SEEK_LEAF_LOOP + template + void forEachLeaf(RadixElement e, Callable &&func) const { + if (e.isNode()) { + e.getNode()->forEachChild( + [&](auto pElement) { forEachLeaf(pElement->load(), func); }); + return; + } + + T *leaf = e.getLeaf(); + if (leaf != nullptr) { + func(RCUPtr::copy(leaf)); + } + } + struct RadixElement { private: union { @@ -351,6 +370,12 @@ } bool isShared() const { return refcount > 0; } + + template void forEachChild(Callable &&func) const { + for (size_t i = 0; i < CHILD_PER_LEVEL; i++) { + func(&children[i]); + } + } }; // Make sure the alignment works for T and RadixElement. 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 @@ -442,4 +442,37 @@ RCULock::synchronize(); } +BOOST_AUTO_TEST_CASE(tree_traversal) { + using E = TestElement; + + RadixTree mytree; + + // Build a vector of elements in ascending key order + std::vector> elements; + elements.reserve(ELEMENTS); + for (uint32_t i = 0; i < ELEMENTS; i++) { + auto ptr = RCUPtr::make(i); + elements.push_back(std::move(ptr)); + } + + // Insert in random order + auto randomizedElements = elements; + Shuffle(randomizedElements.begin(), randomizedElements.end(), + FastRandomContext()); + for (auto &element : randomizedElements) { + BOOST_CHECK(mytree.insert(element)); + } + + // Check the tree is traversed in ascending key order + size_t count = 0; + mytree.forEachLeaf([&](RCUPtr ptr) { + // This test assumes the key is equal to the value + BOOST_CHECK_EQUAL(ptr->getId(), count); + BOOST_CHECK_EQUAL(ptr, elements[count++]); + }); + + // All the elements are parsed + BOOST_CHECK_EQUAL(count, ELEMENTS); +} + BOOST_AUTO_TEST_SUITE_END()