diff --git a/src/radix.h b/src/radix.h --- a/src/radix.h +++ b/src/radix.h @@ -347,7 +347,7 @@ RadixNode &operator=(const RadixNode &) = delete; std::atomic *get(uint32_t level, const K &key) { - return &children[(key >> (level * BITS)) & MASK]; + return &children[T::IndexFromKey(key, level * BITS, MASK)]; } bool isShared() const { return refcount > 0; } 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 @@ -4,6 +4,9 @@ #include +#include +#include + #include #include @@ -17,20 +20,83 @@ template struct TestElement { K key; - TestElement(K keyIn) : key(keyIn) {} + TestElement(K keyIn) : key(std::move(keyIn)) {} const K &getId() const { return key; } IMPLEMENT_RCU_REFCOUNT(uint32_t); }; -template void testInsert() { - using E = TestElement; +template struct TestElementInt : TestElement { + TestElementInt(K keyIn) : TestElement(std::move(keyIn)) {} + + // Required by radix.h + static size_t IndexFromKey(const K &k, uint32_t depth, int mask) { + return (k >> depth) & mask; + } + + // Test only + static K ToKey(K k) { return k; } + + static auto SignedMin() { + return std::numeric_limits::type>::min(); + } + static auto SignedMax() { + return std::numeric_limits::type>::max(); + } + static auto MinusOne() { + return std::numeric_limits::type>::max(); + } + static auto MinusTwo() { + return std::numeric_limits< + typename std::make_unsigned::type>::max() - + 1; + } +}; + +struct TestElementUint256 : TestElement { + TestElementUint256(uint256 keyIn) + : TestElement(std::move(keyIn)) {} + + // Required by radix.h + static size_t IndexFromKey(const uint256 &k, uint32_t depth, int mask) { + auto index = UintToArith256(k) >> depth; + return index.GetLow64() & mask; + } + + // Test only + static uint256 ToKey(const uint256 &k) { return k; } + static uint256 ToKey(uint64_t k) { return ArithToUint256(k); } + + static uint256 SignedMin() { + return uint256S( + "8000000000000000000000000000000000000000000000000000000000000000"); + } + static uint256 SignedMax() { + return uint256S( + "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); + } + static uint256 MinusOne() { + return uint256S( + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); + } + static uint256 MinusTwo() { + return uint256S( + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE"); + } +}; + +template void testInsert() { RadixTree mytree; - auto zero = RCUPtr::make(0); - auto one = RCUPtr::make(1); - auto two = RCUPtr::make(2); - auto three = RCUPtr::make(3); + auto keyZero = E::ToKey(0); + auto keyOne = E::ToKey(1); + auto keyTwo = E::ToKey(2); + auto keyThree = E::ToKey(3); + + auto zero = RCUPtr::make(keyZero); + auto one = RCUPtr::make(keyOne); + auto two = RCUPtr::make(keyTwo); + auto three = RCUPtr::make(keyThree); // Inserting a new element into the tree returns true. BOOST_CHECK(mytree.insert(one)); @@ -49,12 +115,10 @@ BOOST_CHECK(!mytree.insert(three)); // Check extreme values. - using SK = typename std::make_signed::type; - auto maxsigned = RCUPtr::make(std::numeric_limits::max()); - auto minsigned = RCUPtr::make(std::numeric_limits::min()); - using UK = typename std::make_unsigned::type; - auto minusone = RCUPtr::make(std::numeric_limits::max()); - auto minustwo = RCUPtr::make(std::numeric_limits::max() - 1); + auto maxsigned = RCUPtr::make(E::SignedMax()); + auto minsigned = RCUPtr::make(E::SignedMin()); + auto minusone = RCUPtr::make(E::MinusOne()); + auto minustwo = RCUPtr::make(E::MinusTwo()); // Insert them into the tree. BOOST_CHECK(mytree.insert(maxsigned)); @@ -74,62 +138,61 @@ } BOOST_AUTO_TEST_CASE(insert_test) { - testInsert(); - testInsert(); - testInsert(); - testInsert(); + testInsert>(); + testInsert>(); + testInsert>(); + testInsert>(); + + testInsert(); } -template void testGet() { - using E = TestElement; +template void testGet() { RadixTree mytree; - auto zero = RCUPtr::make(0); - auto one = RCUPtr::make(1); - auto two = RCUPtr::make(2); - auto three = RCUPtr::make(3); + auto keyZero = E::ToKey(0); + auto keyOne = E::ToKey(1); + auto keyTwo = E::ToKey(2); + auto keyThree = E::ToKey(3); + + auto zero = RCUPtr::make(keyZero); + auto one = RCUPtr::make(keyOne); + auto two = RCUPtr::make(keyTwo); + auto three = RCUPtr::make(keyThree); // There are no elements in the tree so far. - BOOST_CHECK_EQUAL(mytree.get(1), NULLPTR(E)); + BOOST_CHECK_EQUAL(mytree.get(keyOne), NULLPTR(E)); // Insert an element into the tree and check it is there. BOOST_CHECK(mytree.insert(one)); - BOOST_CHECK_EQUAL(mytree.get(1), one); + BOOST_CHECK_EQUAL(mytree.get(keyOne), one); // Let's insert more elements and check they are recovered properly. - BOOST_CHECK_EQUAL(mytree.get(0), NULLPTR(E)); + BOOST_CHECK_EQUAL(mytree.get(keyZero), NULLPTR(E)); BOOST_CHECK(mytree.insert(zero)); - BOOST_CHECK_EQUAL(mytree.get(0), zero); - BOOST_CHECK_EQUAL(mytree.get(1), one); + BOOST_CHECK_EQUAL(mytree.get(keyZero), zero); + BOOST_CHECK_EQUAL(mytree.get(keyOne), one); // More elements. - BOOST_CHECK_EQUAL(mytree.get(2), NULLPTR(E)); - BOOST_CHECK_EQUAL(mytree.get(3), NULLPTR(E)); + BOOST_CHECK_EQUAL(mytree.get(keyTwo), NULLPTR(E)); + BOOST_CHECK_EQUAL(mytree.get(keyThree), NULLPTR(E)); BOOST_CHECK(mytree.insert(two)); BOOST_CHECK(mytree.insert(three)); - 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); - - // Check extreme values. - using SK = typename std::make_signed::type; - K maxsk = std::numeric_limits::max(); - K minsk = std::numeric_limits::min(); - using UK = typename std::make_unsigned::type; - K maxuk = std::numeric_limits::max(); + BOOST_CHECK_EQUAL(mytree.get(keyZero), zero); + BOOST_CHECK_EQUAL(mytree.get(keyOne), one); + BOOST_CHECK_EQUAL(mytree.get(keyTwo), two); + BOOST_CHECK_EQUAL(mytree.get(keyThree), three); - auto maxsigned = RCUPtr::make(maxsk); - auto minsigned = RCUPtr::make(minsk); - auto minusone = RCUPtr::make(maxuk); - auto minustwo = RCUPtr::make(maxuk - 1); + auto maxsigned = RCUPtr::make(E::SignedMax()); + auto minsigned = RCUPtr::make(E::SignedMin()); + auto minusone = RCUPtr::make(E::MinusOne()); + auto minustwo = RCUPtr::make(E::MinusTwo()); // Check that they are not in the tree. - BOOST_CHECK_EQUAL(mytree.get(maxsk), NULLPTR(E)); - BOOST_CHECK_EQUAL(mytree.get(minsk), NULLPTR(E)); - BOOST_CHECK_EQUAL(mytree.get(maxuk), NULLPTR(E)); - BOOST_CHECK_EQUAL(mytree.get(maxuk - 1), NULLPTR(E)); + BOOST_CHECK_EQUAL(mytree.get(E::SignedMax()), NULLPTR(E)); + BOOST_CHECK_EQUAL(mytree.get(E::SignedMin()), NULLPTR(E)); + BOOST_CHECK_EQUAL(mytree.get(E::MinusOne()), NULLPTR(E)); + BOOST_CHECK_EQUAL(mytree.get(E::MinusTwo()), NULLPTR(E)); // Insert into the tree. BOOST_CHECK(mytree.insert(maxsigned)); @@ -138,39 +201,45 @@ BOOST_CHECK(mytree.insert(minustwo)); // And now they all are in the tree. - 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); - BOOST_CHECK_EQUAL(mytree.get(maxsk), maxsigned); - BOOST_CHECK_EQUAL(mytree.get(minsk), minsigned); - BOOST_CHECK_EQUAL(mytree.get(maxuk), minusone); - BOOST_CHECK_EQUAL(mytree.get(maxuk - 1), minustwo); + BOOST_CHECK_EQUAL(mytree.get(keyZero), zero); + BOOST_CHECK_EQUAL(mytree.get(keyOne), one); + BOOST_CHECK_EQUAL(mytree.get(keyTwo), two); + BOOST_CHECK_EQUAL(mytree.get(keyThree), three); + BOOST_CHECK_EQUAL(mytree.get(E::SignedMax()), maxsigned); + BOOST_CHECK_EQUAL(mytree.get(E::SignedMin()), minsigned); + BOOST_CHECK_EQUAL(mytree.get(E::MinusOne()), minusone); + BOOST_CHECK_EQUAL(mytree.get(E::MinusTwo()), minustwo); } BOOST_AUTO_TEST_CASE(get_test) { - testGet(); - testGet(); - testGet(); - testGet(); + testGet>(); + testGet>(); + testGet>(); + testGet>(); + + testGet(); } -template void testRemove() { - using E = TestElement; +template void testRemove() { RadixTree mytree; - auto zero = RCUPtr::make(0); - auto one = RCUPtr::make(1); - auto two = RCUPtr::make(2); - auto three = RCUPtr::make(3); + auto keyZero = E::ToKey(0); + auto keyOne = E::ToKey(1); + auto keyTwo = E::ToKey(2); + auto keyThree = E::ToKey(3); + + auto zero = RCUPtr::make(keyZero); + auto one = RCUPtr::make(keyOne); + auto two = RCUPtr::make(keyTwo); + auto three = RCUPtr::make(keyThree); // Removing an element that isn't in the tree returns false. - BOOST_CHECK(!mytree.remove(1)); + BOOST_CHECK(!mytree.remove(keyOne)); // 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(E)); + BOOST_CHECK(mytree.remove(keyOne)); + BOOST_CHECK_EQUAL(mytree.get(keyOne), NULLPTR(E)); // Insert several elements and remove them. BOOST_CHECK(mytree.insert(zero)); @@ -178,33 +247,27 @@ 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(mytree.remove(keyZero)); + BOOST_CHECK(mytree.remove(keyOne)); + BOOST_CHECK(mytree.remove(keyTwo)); + BOOST_CHECK(mytree.remove(keyThree)); - BOOST_CHECK_EQUAL(mytree.get(0), NULLPTR(E)); - BOOST_CHECK_EQUAL(mytree.get(1), NULLPTR(E)); - BOOST_CHECK_EQUAL(mytree.get(2), NULLPTR(E)); - BOOST_CHECK_EQUAL(mytree.get(3), NULLPTR(E)); + BOOST_CHECK_EQUAL(mytree.get(keyZero), NULLPTR(E)); + BOOST_CHECK_EQUAL(mytree.get(keyOne), NULLPTR(E)); + BOOST_CHECK_EQUAL(mytree.get(keyTwo), NULLPTR(E)); + BOOST_CHECK_EQUAL(mytree.get(keyThree), NULLPTR(E)); // 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)); + BOOST_CHECK(!mytree.remove(keyZero)); + BOOST_CHECK(!mytree.remove(keyOne)); + BOOST_CHECK(!mytree.remove(keyTwo)); + BOOST_CHECK(!mytree.remove(keyThree)); // Check extreme values. - using SK = typename std::make_signed::type; - K maxsk = std::numeric_limits::max(); - K minsk = std::numeric_limits::min(); - using UK = typename std::make_unsigned::type; - K maxuk = std::numeric_limits::max(); - - auto maxsigned = RCUPtr::make(maxsk); - auto minsigned = RCUPtr::make(minsk); - auto minusone = RCUPtr::make(maxuk); - auto minustwo = RCUPtr::make(maxuk - 1); + auto maxsigned = RCUPtr::make(E::SignedMax()); + auto minsigned = RCUPtr::make(E::SignedMin()); + auto minusone = RCUPtr::make(E::MinusOne()); + auto minustwo = RCUPtr::make(E::MinusTwo()); // Insert them all. BOOST_CHECK(mytree.insert(zero)); @@ -217,34 +280,36 @@ 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(E)); - BOOST_CHECK_EQUAL(mytree.get(1), NULLPTR(E)); - BOOST_CHECK_EQUAL(mytree.get(2), NULLPTR(E)); - BOOST_CHECK_EQUAL(mytree.get(3), NULLPTR(E)); - BOOST_CHECK_EQUAL(mytree.get(maxsk), NULLPTR(E)); - BOOST_CHECK_EQUAL(mytree.get(minsk), NULLPTR(E)); - BOOST_CHECK_EQUAL(mytree.get(maxuk), NULLPTR(E)); - BOOST_CHECK_EQUAL(mytree.get(maxuk - 1), NULLPTR(E)); + BOOST_CHECK(mytree.remove(keyZero)); + BOOST_CHECK(mytree.remove(keyOne)); + BOOST_CHECK(mytree.remove(keyTwo)); + BOOST_CHECK(mytree.remove(keyThree)); + BOOST_CHECK(mytree.remove(E::SignedMax())); + BOOST_CHECK(mytree.remove(E::SignedMin())); + BOOST_CHECK(mytree.remove(E::MinusOne())); + BOOST_CHECK(mytree.remove(E::MinusTwo())); + + BOOST_CHECK_EQUAL(mytree.get(keyZero), NULLPTR(E)); + BOOST_CHECK_EQUAL(mytree.get(keyOne), NULLPTR(E)); + BOOST_CHECK_EQUAL(mytree.get(keyTwo), NULLPTR(E)); + BOOST_CHECK_EQUAL(mytree.get(keyThree), NULLPTR(E)); + BOOST_CHECK_EQUAL(mytree.get(E::SignedMax()), NULLPTR(E)); + BOOST_CHECK_EQUAL(mytree.get(E::SignedMin()), NULLPTR(E)); + BOOST_CHECK_EQUAL(mytree.get(E::MinusOne()), NULLPTR(E)); + BOOST_CHECK_EQUAL(mytree.get(E::MinusTwo()), NULLPTR(E)); } BOOST_AUTO_TEST_CASE(remove_test) { - testRemove(); - testRemove(); - testRemove(); - testRemove(); + testRemove>(); + testRemove>(); + testRemove>(); + testRemove>(); + + testRemove(); } BOOST_AUTO_TEST_CASE(const_element_test) { - using C = const TestElement; + using C = const TestElementInt; RadixTree mytree; BOOST_CHECK(mytree.insert(RCUPtr::make(0))); @@ -278,7 +343,7 @@ BOOST_CHECK(!mytree.remove(3)); } -void CheckConstTree(const RadixTree> &mytree, +void CheckConstTree(const RadixTree> &mytree, bool expected) { BOOST_CHECK_EQUAL(!!mytree.get(0), expected); BOOST_CHECK_EQUAL(!!mytree.get(1), expected); @@ -287,7 +352,7 @@ } BOOST_AUTO_TEST_CASE(const_tree_test) { - using E = TestElement; + using E = TestElementInt; RadixTree mytree; CheckConstTree(mytree, false); @@ -308,7 +373,7 @@ } BOOST_AUTO_TEST_CASE(test_cow) { - using E = TestElement; + using E = TestElementInt; RadixTree mytree; @@ -359,7 +424,7 @@ } BOOST_AUTO_TEST_CASE(test_move) { - using E = TestElement; + using E = TestElementInt; RadixTree mytree; @@ -385,7 +450,7 @@ #define ELEMENTS 65536 BOOST_AUTO_TEST_CASE(insert_stress_test) { - using E = TestElement; + using E = TestElementInt; RadixTree mytree; std::atomic success{0};