diff --git a/.github/workflows/code_sanitizers.yml b/.github/workflows/code_sanitizers.yml index 1a6ae97b7f..9e9e7d6cda 100644 --- a/.github/workflows/code_sanitizers.yml +++ b/.github/workflows/code_sanitizers.yml @@ -16,7 +16,7 @@ jobs: - { name: ASAN, ignore_errors: false, leak_check: false } - { name: ASAN_INT, ignore_errors: true, leak_check: false } - { name: TSAN, ignore_errors: false } - - { name: LEAK, ignore_errors: true, leak_check: true } + - { name: LEAK, ignore_errors: false, leak_check: true } exclude: # Bug when running with TSAN: "ThreadSanitizer: CHECK failed: sanitizer_deadlock_detector" - BACKEND: rocksdb diff --git a/nano/core_test/CMakeLists.txt b/nano/core_test/CMakeLists.txt index e5b47c9e43..2db941e69c 100644 --- a/nano/core_test/CMakeLists.txt +++ b/nano/core_test/CMakeLists.txt @@ -35,10 +35,12 @@ add_executable( network_filter.cpp network_functions.cpp node.cpp + numbers.cpp object_stream.cpp optimistic_scheduler.cpp processing_queue.cpp processor_service.cpp + random_pool.cpp rep_crawler.cpp receivable.cpp peer_history.cpp @@ -51,10 +53,10 @@ add_executable( socket.cpp system.cpp telemetry.cpp + thread_pool.cpp throttle.cpp toml.cpp timer.cpp - uint256_union.cpp unchecked_map.cpp utility.cpp vote_cache.cpp diff --git a/nano/core_test/block_store.cpp b/nano/core_test/block_store.cpp index 84e98380c0..491741708f 100644 --- a/nano/core_test/block_store.cpp +++ b/nano/core_test/block_store.cpp @@ -798,7 +798,7 @@ TEST (block_store, large_iteration) // Reverse iteration std::unordered_set accounts3; previous = std::numeric_limits::max (); - for (auto i (store->account.rbegin (transaction)), n (store->account.end (transaction)); i != n; --i) + for (auto i (store->account.rbegin (transaction)), n (store->account.rend (transaction)); i != n; ++i) { nano::account current (i->first); ASSERT_LT (current.number (), previous.number ()); @@ -1254,7 +1254,7 @@ TEST (block_store, online_weight) auto transaction (store->tx_begin_write ()); ASSERT_EQ (0, store->online_weight.count (transaction)); ASSERT_EQ (store->online_weight.end (transaction), store->online_weight.begin (transaction)); - ASSERT_EQ (store->online_weight.end (transaction), store->online_weight.rbegin (transaction)); + ASSERT_EQ (store->online_weight.rend (transaction), store->online_weight.rbegin (transaction)); store->online_weight.put (transaction, 1, 2); store->online_weight.put (transaction, 3, 4); } @@ -1266,18 +1266,18 @@ TEST (block_store, online_weight) ASSERT_EQ (1, item->first); ASSERT_EQ (2, item->second.number ()); auto item_last (store->online_weight.rbegin (transaction)); - ASSERT_NE (store->online_weight.end (transaction), item_last); + ASSERT_NE (store->online_weight.rend (transaction), item_last); ASSERT_EQ (3, item_last->first); ASSERT_EQ (4, item_last->second.number ()); store->online_weight.del (transaction, 1); ASSERT_EQ (1, store->online_weight.count (transaction)); - ASSERT_EQ (store->online_weight.begin (transaction), store->online_weight.rbegin (transaction)); + ASSERT_EQ (*store->online_weight.begin (transaction), *store->online_weight.rbegin (transaction)); store->online_weight.del (transaction, 3); } auto transaction (store->tx_begin_read ()); ASSERT_EQ (0, store->online_weight.count (transaction)); ASSERT_EQ (store->online_weight.end (transaction), store->online_weight.begin (transaction)); - ASSERT_EQ (store->online_weight.end (transaction), store->online_weight.rbegin (transaction)); + ASSERT_EQ (store->online_weight.rend (transaction), store->online_weight.rbegin (transaction)); } TEST (block_store, pruned_blocks) diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index 23d3d751b1..0dd6e786db 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -2406,7 +2406,7 @@ TEST (ledger, state_account) .work (*pool.generate (nano::dev::genesis->hash ())) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send1)); - ASSERT_EQ (nano::dev::genesis_key.pub, ledger.any.block_account (transaction, send1->hash ())); + ASSERT_EQ (nano::dev::genesis_key.pub, ledger.any.block_account (transaction, send1->hash ()).value ()); } TEST (ledger, state_send_receive) diff --git a/nano/core_test/uint256_union.cpp b/nano/core_test/numbers.cpp similarity index 75% rename from nano/core_test/uint256_union.cpp rename to nano/core_test/numbers.cpp index 743d0fe1c7..a30b2eb108 100644 --- a/nano/core_test/uint256_union.cpp +++ b/nano/core_test/numbers.cpp @@ -1,27 +1,192 @@ -#include +#include #include -#include #include +#include + #include +#include + +TEST (numbers, identity) +{ + ASSERT_EQ (1, nano::uint128_union (1).number ().convert_to ()); + ASSERT_EQ (1, nano::uint256_union (1).number ().convert_to ()); + ASSERT_EQ (1, nano::uint512_union (1).number ().convert_to ()); +} namespace { -template -void assert_union_types (); +template +void check_operator_less_than (Type lhs, Type rhs) +{ + ASSERT_TRUE (lhs < rhs); + ASSERT_FALSE (rhs < lhs); + ASSERT_FALSE (lhs < lhs); + ASSERT_FALSE (rhs < rhs); +} + +template +void test_operator_less_than () +{ + using underlying_t = typename Type::underlying_type; + + // Small + check_operator_less_than (Type{ 123 }, Type{ 124 }); + check_operator_less_than (Type{ 124 }, Type{ 125 }); + + // Medium + check_operator_less_than (Type{ std::numeric_limits::max () - 1 }, Type{ std::numeric_limits::max () + 1 }); + check_operator_less_than (Type{ std::numeric_limits::max () - 12345678 }, Type{ std::numeric_limits::max () - 123456 }); + + // Large + check_operator_less_than (Type{ std::numeric_limits::max () - 555555555555 }, Type{ std::numeric_limits::max () - 1 }); + + // Boundary values + check_operator_less_than (Type{ std::numeric_limits::min () }, Type{ std::numeric_limits::max () }); +} + +template +void check_operator_greater_than (Type lhs, Type rhs) +{ + ASSERT_TRUE (lhs > rhs); + ASSERT_FALSE (rhs > lhs); + ASSERT_FALSE (lhs > lhs); + ASSERT_FALSE (rhs > rhs); +} + +template +void test_operator_greater_than () +{ + using underlying_t = typename Type::underlying_type; -template -void test_union_operator_less_than (); + // Small + check_operator_greater_than (Type{ 124 }, Type{ 123 }); + check_operator_greater_than (Type{ 125 }, Type{ 124 }); -template -void check_operator_less_than (Num lhs, Num rhs); + // Medium + check_operator_greater_than (Type{ std::numeric_limits::max () + 1 }, Type{ std::numeric_limits::max () - 1 }); + check_operator_greater_than (Type{ std::numeric_limits::max () - 123456 }, Type{ std::numeric_limits::max () - 12345678 }); -template -void test_union_operator_greater_than (); + // Large + check_operator_greater_than (Type{ std::numeric_limits::max () - 1 }, Type{ std::numeric_limits::max () - 555555555555 }); -template -void check_operator_greater_than (Num lhs, Num rhs); + // Boundary values + check_operator_greater_than (Type{ std::numeric_limits::max () }, Type{ std::numeric_limits::min () }); +} + +template +void test_comparison () +{ + test_operator_less_than (); + test_operator_greater_than (); +} +} + +TEST (numbers, comparison) +{ + test_comparison (); + test_comparison (); + test_comparison (); + test_comparison (); + test_comparison (); + test_comparison (); + test_comparison (); + test_comparison (); + test_comparison (); + test_comparison (); + test_comparison (); +} + +namespace +{ +template class Hash> +void test_hashing () +{ + Hash hash; + using underlying_t = typename Type::underlying_type; + + // Basic equality tests + ASSERT_EQ (hash (Type{}), hash (Type{})); + ASSERT_EQ (hash (Type{ 123 }), hash (Type{ 123 })); + + // Basic inequality tests + ASSERT_NE (hash (Type{ 123 }), hash (Type{ 124 })); + ASSERT_NE (hash (Type{ 0 }), hash (Type{ 1 })); + + // Boundary value tests + constexpr auto min_val = std::numeric_limits::min (); + constexpr auto max_val = std::numeric_limits::max (); + + // Min/Max tests + ASSERT_EQ (hash (Type{ min_val }), hash (Type{ min_val })); + ASSERT_EQ (hash (Type{ max_val }), hash (Type{ max_val })); + ASSERT_NE (hash (Type{ min_val }), hash (Type{ max_val })); + + // Near boundary tests + ASSERT_NE (hash (Type{ min_val }), hash (Type{ min_val + 1 })); + ASSERT_NE (hash (Type{ max_val }), hash (Type{ max_val - 1 })); + ASSERT_NE (hash (Type{ min_val + 1 }), hash (Type{ max_val })); + ASSERT_NE (hash (Type{ max_val - 1 }), hash (Type{ min_val })); + + // Common value tests + std::vector common_values = { + 0, // Zero + 1, // One + 42, // Common test value + 0xFF, // Byte boundary + 0xFFFF, // Word boundary + min_val, // Minimum + max_val, // Maximum + max_val / 2, // Middle value + min_val + (max_val / 2) // Offset middle + }; + + // Test all common values against each other + for (size_t i = 0; i < common_values.size (); ++i) + { + for (size_t j = i + 1; j < common_values.size (); ++j) + { + if (common_values[i] != common_values[j]) + { + ASSERT_NE (hash (Type{ common_values[i] }), hash (Type{ common_values[j] })); + } + else + { + ASSERT_EQ (hash (Type{ common_values[i] }), hash (Type{ common_values[j] })); + } + } + } +} +} + +TEST (numbers, hashing) +{ + // Using std::hash + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + + // Using boost::hash + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); } TEST (uint128_union, decode_dec) @@ -64,16 +229,6 @@ TEST (uint128_union, decode_dec_overflow) ASSERT_TRUE (error); } -TEST (uint128_union, operator_less_than) -{ - test_union_operator_less_than (); -} - -TEST (uint128_union, operator_greater_than) -{ - test_union_operator_greater_than (); -} - struct test_punct : std::moneypunct { pattern do_pos_format () const @@ -149,13 +304,6 @@ TEST (uint128_union, decode_decimal) ASSERT_EQ (1230 * nano::Knano_ratio, amount.number ()); } -TEST (unions, identity) -{ - ASSERT_EQ (1, nano::uint128_union (1).number ().convert_to ()); - ASSERT_EQ (1, nano::uint256_union (1).number ().convert_to ()); - ASSERT_EQ (1, nano::uint512_union (1).number ().convert_to ()); -} - TEST (uint256_union, key_encryption) { nano::keypair key1; @@ -449,11 +597,6 @@ TEST (uint256_union, bounds) ASSERT_TRUE (key.decode_account (bad2)); } -TEST (uint256_union, operator_less_than) -{ - test_union_operator_less_than (); -} - TEST (uint64_t, parse) { uint64_t value0 (1); @@ -504,114 +647,4 @@ TEST (uint512_union, hash) ASSERT_NE (h (x1), h (x2)); } } -} - -namespace -{ -template -void assert_union_types () -{ - static_assert ((std::is_same::value && std::is_same::value) || (std::is_same::value && std::is_same::value) || (std::is_same::value && std::is_same::value), - "Union type needs to be consistent with the lower/upper Bound type"); -} - -template -void test_union_operator_less_than () -{ - assert_union_types (); - - // Small - check_operator_less_than (Union (123), Union (124)); - check_operator_less_than (Union (124), Union (125)); - - // Medium - check_operator_less_than (Union (std::numeric_limits::max () - 1), Union (std::numeric_limits::max () + 1)); - check_operator_less_than (Union (std::numeric_limits::max () - 12345678), Union (std::numeric_limits::max () - 123456)); - - // Large - check_operator_less_than (Union (std::numeric_limits::max () - 555555555555), Union (std::numeric_limits::max () - 1)); - - // Boundary values - check_operator_less_than (Union (std::numeric_limits::min ()), Union (std::numeric_limits::max ())); -} - -template -void check_operator_less_than (Num lhs, Num rhs) -{ - ASSERT_TRUE (lhs < rhs); - ASSERT_FALSE (rhs < lhs); - ASSERT_FALSE (lhs < lhs); - ASSERT_FALSE (rhs < rhs); -} - -template -void test_union_operator_greater_than () -{ - assert_union_types (); - - // Small - check_operator_greater_than (Union (124), Union (123)); - check_operator_greater_than (Union (125), Union (124)); - - // Medium - check_operator_greater_than (Union (std::numeric_limits::max () + 1), Union (std::numeric_limits::max () - 1)); - check_operator_greater_than (Union (std::numeric_limits::max () - 123456), Union (std::numeric_limits::max () - 12345678)); - - // Large - check_operator_greater_than (Union (std::numeric_limits::max () - 1), Union (std::numeric_limits::max () - 555555555555)); - - // Boundary values - check_operator_greater_than (Union (std::numeric_limits::max ()), Union (std::numeric_limits::min ())); -} - -template -void check_operator_greater_than (Num lhs, Num rhs) -{ - ASSERT_TRUE (lhs > rhs); - ASSERT_FALSE (rhs > lhs); - ASSERT_FALSE (lhs > lhs); - ASSERT_FALSE (rhs > rhs); -} -} - -TEST (random_pool, multithreading) -{ - std::vector threads; - for (auto i = 0; i < 100; ++i) - { - threads.emplace_back ([] () { - nano::uint256_union number; - nano::random_pool::generate_block (number.bytes.data (), number.bytes.size ()); - }); - } - for (auto & i : threads) - { - i.join (); - } -} - -// Test that random 64bit numbers are within the given range -TEST (random_pool, generate_word64) -{ - int occurrences[10] = { 0 }; - for (auto i = 0; i < 1000; ++i) - { - auto random = nano::random_pool::generate_word64 (1, 9); - ASSERT_TRUE (random >= 1 && random <= 9); - occurrences[random] += 1; - } - - for (auto i = 1; i < 10; ++i) - { - ASSERT_GT (occurrences[i], 0); - } -} - -// Test random numbers > uint32 max -TEST (random_pool, generate_word64_big_number) -{ - uint64_t min = static_cast (std::numeric_limits::max ()) + 1; - uint64_t max = std::numeric_limits::max (); - auto big_random = nano::random_pool::generate_word64 (min, max); - ASSERT_GE (big_random, min); -} +} \ No newline at end of file diff --git a/nano/core_test/random_pool.cpp b/nano/core_test/random_pool.cpp new file mode 100644 index 0000000000..227ea87fcb --- /dev/null +++ b/nano/core_test/random_pool.cpp @@ -0,0 +1,48 @@ +#include +#include + +#include + +#include + +TEST (random_pool, multithreading) +{ + std::vector threads; + for (auto i = 0; i < 100; ++i) + { + threads.emplace_back ([] () { + nano::uint256_union number; + nano::random_pool::generate_block (number.bytes.data (), number.bytes.size ()); + }); + } + for (auto & i : threads) + { + i.join (); + } +} + +// Test that random 64bit numbers are within the given range +TEST (random_pool, generate_word64) +{ + int occurrences[10] = { 0 }; + for (auto i = 0; i < 1000; ++i) + { + auto random = nano::random_pool::generate_word64 (1, 9); + ASSERT_TRUE (random >= 1 && random <= 9); + occurrences[random] += 1; + } + + for (auto i = 1; i < 10; ++i) + { + ASSERT_GT (occurrences[i], 0); + } +} + +// Test random numbers > uint32 max +TEST (random_pool, generate_word64_big_number) +{ + uint64_t min = static_cast (std::numeric_limits::max ()) + 1; + uint64_t max = std::numeric_limits::max (); + auto big_random = nano::random_pool::generate_word64 (min, max); + ASSERT_GE (big_random, min); +} diff --git a/nano/core_test/thread_pool.cpp b/nano/core_test/thread_pool.cpp new file mode 100644 index 0000000000..810bbd0ac5 --- /dev/null +++ b/nano/core_test/thread_pool.cpp @@ -0,0 +1,96 @@ +#include +#include +#include + +#include + +#include + +TEST (thread_pool, thread_pool) +{ + std::atomic passed_sleep{ false }; + + auto func = [&passed_sleep] () { + std::this_thread::sleep_for (std::chrono::seconds (1)); + passed_sleep = true; + }; + + nano::thread_pool workers (1u, nano::thread_role::name::unknown); + nano::test::start_stop_guard stop_guard{ workers }; + workers.post (func); + ASSERT_FALSE (passed_sleep); + + nano::timer timer_l; + timer_l.start (); + while (!passed_sleep) + { + if (timer_l.since_start () > std::chrono::seconds (10)) + { + break; + } + } + ASSERT_TRUE (passed_sleep); +} + +TEST (thread_pool, one) +{ + std::atomic done (false); + nano::mutex mutex; + nano::condition_variable condition; + nano::thread_pool workers (1u, nano::thread_role::name::unknown); + nano::test::start_stop_guard stop_guard{ workers }; + workers.post ([&] () { + { + nano::lock_guard lock{ mutex }; + done = true; + } + condition.notify_one (); + }); + nano::unique_lock unique{ mutex }; + condition.wait (unique, [&] () { return !!done; }); +} + +TEST (thread_pool, many) +{ + std::atomic count (0); + nano::mutex mutex; + nano::condition_variable condition; + nano::thread_pool workers (50u, nano::thread_role::name::unknown); + nano::test::start_stop_guard stop_guard{ workers }; + for (auto i (0); i < 50; ++i) + { + workers.post ([&] () { + { + nano::lock_guard lock{ mutex }; + count += 1; + } + condition.notify_one (); + }); + } + nano::unique_lock unique{ mutex }; + condition.wait (unique, [&] () { return count == 50; }); +} + +TEST (thread_pool, top_execution) +{ + int value1 (0); + int value2 (0); + nano::mutex mutex; + std::promise promise; + nano::thread_pool workers (1u, nano::thread_role::name::unknown); + nano::test::start_stop_guard stop_guard{ workers }; + workers.post ([&] () { + nano::lock_guard lock{ mutex }; + value1 = 1; + value2 = 1; + }); + workers.post_delayed (std::chrono::milliseconds (1), [&] () { + nano::lock_guard lock{ mutex }; + value2 = 2; + promise.set_value (false); + }); + promise.get_future ().get (); + nano::lock_guard lock{ mutex }; + ASSERT_EQ (1, value1); + ASSERT_EQ (2, value2); +} \ No newline at end of file diff --git a/nano/core_test/utility.cpp b/nano/core_test/utility.cpp index 730714dc83..30ee29544e 100644 --- a/nano/core_test/utility.cpp +++ b/nano/core_test/utility.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include @@ -146,91 +146,6 @@ TEST (optional_ptr, basic) ASSERT_EQ (opt->z, 3); } -TEST (thread, thread_pool) -{ - std::atomic passed_sleep{ false }; - - auto func = [&passed_sleep] () { - std::this_thread::sleep_for (std::chrono::seconds (1)); - passed_sleep = true; - }; - - nano::thread_pool workers (1u, nano::thread_role::name::unknown); - workers.push_task (func); - ASSERT_FALSE (passed_sleep); - - nano::timer timer_l; - timer_l.start (); - while (!passed_sleep) - { - if (timer_l.since_start () > std::chrono::seconds (10)) - { - break; - } - } - ASSERT_TRUE (passed_sleep); -} - -TEST (thread_pool_alarm, one) -{ - std::atomic done (false); - nano::mutex mutex; - nano::condition_variable condition; - nano::thread_pool workers (1u, nano::thread_role::name::unknown); - workers.add_timed_task (std::chrono::steady_clock::now (), [&] () { - { - nano::lock_guard lock{ mutex }; - done = true; - } - condition.notify_one (); - }); - nano::unique_lock unique{ mutex }; - condition.wait (unique, [&] () { return !!done; }); -} - -TEST (thread_pool_alarm, many) -{ - std::atomic count (0); - nano::mutex mutex; - nano::condition_variable condition; - nano::thread_pool workers (50u, nano::thread_role::name::unknown); - for (auto i (0); i < 50; ++i) - { - workers.add_timed_task (std::chrono::steady_clock::now (), [&] () { - { - nano::lock_guard lock{ mutex }; - count += 1; - } - condition.notify_one (); - }); - } - nano::unique_lock unique{ mutex }; - condition.wait (unique, [&] () { return count == 50; }); -} - -TEST (thread_pool_alarm, top_execution) -{ - int value1 (0); - int value2 (0); - nano::mutex mutex; - std::promise promise; - nano::thread_pool workers (1u, nano::thread_role::name::unknown); - workers.add_timed_task (std::chrono::steady_clock::now (), [&] () { - nano::lock_guard lock{ mutex }; - value1 = 1; - value2 = 1; - }); - workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::milliseconds (1), [&] () { - nano::lock_guard lock{ mutex }; - value2 = 2; - promise.set_value (false); - }); - promise.get_future ().get (); - nano::lock_guard lock{ mutex }; - ASSERT_EQ (1, value1); - ASSERT_EQ (2, value2); -} - TEST (filesystem, remove_all_files) { auto path = nano::unique_path (); diff --git a/nano/lib/CMakeLists.txt b/nano/lib/CMakeLists.txt index 702cb84fa3..76d53944f0 100644 --- a/nano/lib/CMakeLists.txt +++ b/nano/lib/CMakeLists.txt @@ -94,7 +94,6 @@ add_library( stats_sinks.hpp stream.hpp thread_pool.hpp - thread_pool.cpp thread_roles.hpp thread_roles.cpp thread_runner.hpp diff --git a/nano/lib/blocks.cpp b/nano/lib/blocks.cpp index a4c21b8678..4fc527eb0d 100644 --- a/nano/lib/blocks.cpp +++ b/nano/lib/blocks.cpp @@ -609,7 +609,7 @@ std::optional nano::send_block::destination_field () const return hashables.destination; } -nano::root const & nano::send_block::root () const +nano::root nano::send_block::root () const { return hashables.previous; } @@ -899,7 +899,7 @@ std::optional nano::open_block::source_field () const return hashables.source; } -nano::root const & nano::open_block::root () const +nano::root nano::open_block::root () const { return hashables.account; } @@ -1165,7 +1165,7 @@ bool nano::change_block::valid_predecessor (nano::block const & block_a) const return result; } -nano::root const & nano::change_block::root () const +nano::root nano::change_block::root () const { return hashables.previous; } @@ -1482,7 +1482,7 @@ bool nano::state_block::valid_predecessor (nano::block const & block_a) const return true; } -nano::root const & nano::state_block::root () const +nano::root nano::state_block::root () const { if (!hashables.previous.is_zero ()) { @@ -1836,7 +1836,7 @@ std::optional nano::receive_block::source_field () const return hashables.source; } -nano::root const & nano::receive_block::root () const +nano::root nano::receive_block::root () const { return hashables.previous; } diff --git a/nano/lib/blocks.hpp b/nano/lib/blocks.hpp index 1ddb8302dd..2312855cb6 100644 --- a/nano/lib/blocks.hpp +++ b/nano/lib/blocks.hpp @@ -33,7 +33,7 @@ class block virtual uint64_t block_work () const = 0; virtual void block_work_set (uint64_t) = 0; // Previous block or account number for open blocks - virtual nano::root const & root () const = 0; + virtual nano::root root () const = 0; // Qualified root value based on previous() and root() virtual nano::qualified_root qualified_root () const; virtual void serialize (nano::stream &) const = 0; @@ -123,7 +123,7 @@ class send_block : public nano::block virtual ~send_block () = default; uint64_t block_work () const override; void block_work_set (uint64_t) override; - nano::root const & root () const override; + nano::root root () const override; void serialize (nano::stream &) const override; bool deserialize (nano::stream &); void serialize_json (std::string &, bool = false) const override; @@ -177,7 +177,7 @@ class receive_block : public nano::block virtual ~receive_block () = default; uint64_t block_work () const override; void block_work_set (uint64_t) override; - nano::root const & root () const override; + nano::root root () const override; void serialize (nano::stream &) const override; bool deserialize (nano::stream &); void serialize_json (std::string &, bool = false) const override; @@ -232,7 +232,7 @@ class open_block : public nano::block virtual ~open_block () = default; uint64_t block_work () const override; void block_work_set (uint64_t) override; - nano::root const & root () const override; + nano::root root () const override; void serialize (nano::stream &) const override; bool deserialize (nano::stream &); void serialize_json (std::string &, bool = false) const override; @@ -287,7 +287,7 @@ class change_block : public nano::block virtual ~change_block () = default; uint64_t block_work () const override; void block_work_set (uint64_t) override; - nano::root const & root () const override; + nano::root root () const override; void serialize (nano::stream &) const override; bool deserialize (nano::stream &); void serialize_json (std::string &, bool = false) const override; @@ -353,7 +353,7 @@ class state_block : public nano::block virtual ~state_block () = default; uint64_t block_work () const override; void block_work_set (uint64_t) override; - nano::root const & root () const override; + nano::root root () const override; void serialize (nano::stream &) const override; bool deserialize (nano::stream &); void serialize_json (std::string &, bool = false) const override; diff --git a/nano/lib/numbers.cpp b/nano/lib/numbers.cpp index f33d6f3cb3..916329b673 100644 --- a/nano/lib/numbers.cpp +++ b/nano/lib/numbers.cpp @@ -61,11 +61,6 @@ std::string nano::public_key::to_account () const return result; } -nano::public_key::public_key () : - uint256_union{ 0 } -{ -} - nano::public_key const & nano::public_key::null () { return nano::hardened_constants::get ().not_an_account; @@ -143,12 +138,6 @@ bool nano::public_key::decode_account (std::string const & source_a) return error; } -nano::uint256_union::uint256_union (nano::uint256_t const & number_a) -{ - bytes.fill (0); - boost::multiprecision::export_bits (number_a, bytes.rbegin (), 8, false); -} - // Construct a uint256_union = AES_ENC_CTR (cleartext, key, iv) void nano::uint256_union::encrypt (nano::raw_key const & cleartext, nano::raw_key const & key, uint128_union const & iv) { @@ -157,11 +146,6 @@ void nano::uint256_union::encrypt (nano::raw_key const & cleartext, nano::raw_ke enc.ProcessData (bytes.data (), cleartext.bytes.data (), sizeof (cleartext.bytes)); } -bool nano::uint256_union::is_zero () const -{ - return qwords[0] == 0 && qwords[1] == 0 && qwords[2] == 0 && qwords[3] == 0; -} - std::string nano::uint256_union::to_string () const { std::string result; @@ -193,22 +177,9 @@ nano::uint256_union nano::uint256_union::operator^ (nano::uint256_union const & nano::uint256_union::uint256_union (std::string const & hex_a) { auto error (decode_hex (hex_a)); - release_assert (!error); } -void nano::uint256_union::clear () -{ - qwords.fill (0); -} - -nano::uint256_t nano::uint256_union::number () const -{ - nano::uint256_t result; - boost::multiprecision::import_bits (result, bytes.begin (), bytes.end ()); - return result; -} - void nano::uint256_union::encode_hex (std::string & text) const { debug_assert (text.empty ()); @@ -281,46 +252,6 @@ bool nano::uint256_union::decode_dec (std::string const & text) return error; } -nano::uint256_union::uint256_union (uint64_t value0) -{ - *this = nano::uint256_t (value0); -} - -bool nano::uint512_union::operator== (nano::uint512_union const & other_a) const -{ - return bytes == other_a.bytes; -} - -nano::uint512_union::uint512_union (nano::uint256_union const & upper, nano::uint256_union const & lower) -{ - uint256s[0] = upper; - uint256s[1] = lower; -} - -nano::uint512_union::uint512_union (nano::uint512_t const & number_a) -{ - bytes.fill (0); - boost::multiprecision::export_bits (number_a, bytes.rbegin (), 8, false); -} - -bool nano::uint512_union::is_zero () const -{ - return qwords[0] == 0 && qwords[1] == 0 && qwords[2] == 0 && qwords[3] == 0 - && qwords[4] == 0 && qwords[5] == 0 && qwords[6] == 0 && qwords[7] == 0; -} - -void nano::uint512_union::clear () -{ - bytes.fill (0); -} - -nano::uint512_t nano::uint512_union::number () const -{ - nano::uint512_t result; - boost::multiprecision::import_bits (result, bytes.begin (), bytes.end ()); - return result; -} - void nano::uint512_union::encode_hex (std::string & text) const { debug_assert (text.empty ()); @@ -355,18 +286,6 @@ bool nano::uint512_union::decode_hex (std::string const & text) return error; } -bool nano::uint512_union::operator!= (nano::uint512_union const & other_a) const -{ - return !(*this == other_a); -} - -nano::uint512_union & nano::uint512_union::operator^= (nano::uint512_union const & other_a) -{ - uint256s[0] ^= other_a.uint256s[0]; - uint256s[1] ^= other_a.uint256s[1]; - return *this; -} - std::string nano::uint512_union::to_string () const { std::string result; @@ -431,48 +350,9 @@ bool nano::validate_message (nano::public_key const & public_key, nano::uint256_ nano::uint128_union::uint128_union (std::string const & string_a) { auto error (decode_hex (string_a)); - release_assert (!error); } -nano::uint128_union::uint128_union (uint64_t value_a) -{ - *this = nano::uint128_t (value_a); -} - -nano::uint128_union::uint128_union (nano::uint128_t const & number_a) -{ - bytes.fill (0); - boost::multiprecision::export_bits (number_a, bytes.rbegin (), 8, false); -} - -bool nano::uint128_union::operator== (nano::uint128_union const & other_a) const -{ - return qwords[0] == other_a.qwords[0] && qwords[1] == other_a.qwords[1]; -} - -bool nano::uint128_union::operator!= (nano::uint128_union const & other_a) const -{ - return !(*this == other_a); -} - -bool nano::uint128_union::operator< (nano::uint128_union const & other_a) const -{ - return std::memcmp (bytes.data (), other_a.bytes.data (), 16) < 0; -} - -bool nano::uint128_union::operator> (nano::uint128_union const & other_a) const -{ - return std::memcmp (bytes.data (), other_a.bytes.data (), 16) > 0; -} - -nano::uint128_t nano::uint128_union::number () const -{ - nano::uint128_t result; - boost::multiprecision::import_bits (result, bytes.begin (), bytes.end ()); - return result; -} - void nano::uint128_union::encode_hex (std::string & text) const { debug_assert (text.empty ()); @@ -747,16 +627,6 @@ std::string nano::uint128_union::format_balance (nano::uint128_t scale, int prec return ::format_balance (number (), scale, precision, group_digits, thousands_sep, decimal_point, grouping); } -void nano::uint128_union::clear () -{ - qwords.fill (0); -} - -bool nano::uint128_union::is_zero () const -{ - return qwords[0] == 0 && qwords[1] == 0; -} - std::string nano::uint128_union::to_string () const { std::string result; @@ -771,26 +641,6 @@ std::string nano::uint128_union::to_string_dec () const return result; } -nano::hash_or_account::hash_or_account () : - account{} -{ -} - -nano::hash_or_account::hash_or_account (uint64_t value_a) : - raw (value_a) -{ -} - -bool nano::hash_or_account::is_zero () const -{ - return raw.is_zero (); -} - -void nano::hash_or_account::clear () -{ - raw.clear (); -} - bool nano::hash_or_account::decode_hex (std::string const & text_a) { return raw.decode_hex (text_a); @@ -811,36 +661,6 @@ std::string nano::hash_or_account::to_account () const return account.to_account (); } -nano::block_hash const & nano::hash_or_account::as_block_hash () const -{ - return hash; -} - -nano::account const & nano::hash_or_account::as_account () const -{ - return account; -} - -nano::hash_or_account::operator nano::uint256_union const & () const -{ - return raw; -} - -nano::block_hash const & nano::root::previous () const -{ - return hash; -} - -bool nano::hash_or_account::operator== (nano::hash_or_account const & hash_or_account_a) const -{ - return bytes == hash_or_account_a.bytes; -} - -bool nano::hash_or_account::operator!= (nano::hash_or_account const & hash_or_account_a) const -{ - return !(*this == hash_or_account_a); -} - std::string nano::to_string_hex (uint64_t const value_a) { std::stringstream stream; @@ -915,6 +735,13 @@ std::ostream & nano::operator<< (std::ostream & os, const uint512_union & val) return os; } +std::ostream & nano::operator<< (std::ostream & os, const hash_or_account & val) +{ + // TODO: Replace with streaming implementation + os << val.to_string (); + return os; +} + #ifdef _WIN32 #pragma warning(push) #pragma warning(disable : 4146) // warning C4146: unary minus operator applied to unsigned type, result still unsigned @@ -947,43 +774,3 @@ double nano::difficulty::to_multiplier (uint64_t const difficulty_a, uint64_t co #ifdef _WIN32 #pragma warning(pop) #endif - -nano::public_key::operator nano::link const & () const -{ - return reinterpret_cast (*this); -} - -nano::public_key::operator nano::root const & () const -{ - return reinterpret_cast (*this); -} - -nano::public_key::operator nano::hash_or_account const & () const -{ - return reinterpret_cast (*this); -} - -bool nano::public_key::operator== (std::nullptr_t) const -{ - return bytes == null ().bytes; -} - -bool nano::public_key::operator!= (std::nullptr_t) const -{ - return !(*this == nullptr); -} - -nano::block_hash::operator nano::link const & () const -{ - return reinterpret_cast (*this); -} - -nano::block_hash::operator nano::root const & () const -{ - return reinterpret_cast (*this); -} - -nano::block_hash::operator nano::hash_or_account const & () const -{ - return reinterpret_cast (*this); -} diff --git a/nano/lib/numbers.hpp b/nano/lib/numbers.hpp index c637e1594b..bf8137c657 100644 --- a/nano/lib/numbers.hpp +++ b/nano/lib/numbers.hpp @@ -4,6 +4,8 @@ #include #include +#include +#include #include #include @@ -13,6 +15,7 @@ namespace nano using uint128_t = boost::multiprecision::uint128_t; using uint256_t = boost::multiprecision::uint256_t; using uint512_t = boost::multiprecision::uint512_t; + // SI dividers nano::uint128_t const Knano_ratio = nano::uint128_t ("1000000000000000000000000000000000"); // 10^33 = 1000 nano nano::uint128_t const nano_ratio = nano::uint128_t ("1000000000000000000000000000000"); // 10^30 = 1 nano @@ -20,31 +23,55 @@ nano::uint128_t const raw_ratio = nano::uint128_t ("1"); // 10^0 class uint128_union { +public: + // Type that is implicitly convertible to this union + using underlying_type = nano::uint128_t; + public: uint128_union () = default; + uint128_union (uint64_t value) : + uint128_union (nano::uint128_t{ value }){}; + uint128_union (nano::uint128_t const & value) + { + bytes.fill (0); + boost::multiprecision::export_bits (value, bytes.rbegin (), 8, false); + } + /** * Decode from hex string * @warning Aborts at runtime if the input is invalid */ - uint128_union (std::string const &); - uint128_union (uint64_t); - uint128_union (nano::uint128_t const &); - bool operator== (nano::uint128_union const &) const; - bool operator!= (nano::uint128_union const &) const; - bool operator< (nano::uint128_union const &) const; - bool operator> (nano::uint128_union const &) const; + explicit uint128_union (std::string const &); + void encode_hex (std::string &) const; bool decode_hex (std::string const &); void encode_dec (std::string &) const; bool decode_dec (std::string const &, bool = false); bool decode_dec (std::string const &, nano::uint128_t); + std::string format_balance (nano::uint128_t scale, int precision, bool group_digits) const; std::string format_balance (nano::uint128_t scale, int precision, bool group_digits, std::locale const & locale) const; - nano::uint128_t number () const; - void clear (); - bool is_zero () const; + + void clear () + { + qwords.fill (0); + } + bool is_zero () const + { + return qwords[0] == 0 && qwords[1] == 0; + } + + nano::uint128_t number () const + { + nano::uint128_t result; + boost::multiprecision::import_bits (result, bytes.begin (), bytes.end ()); + return result; + } + std::string to_string () const; std::string to_string_dec () const; + +public: union { std::array bytes; @@ -52,6 +79,24 @@ class uint128_union std::array dwords; std::array qwords; }; + +public: // Keep operators inlined + std::strong_ordering operator<=> (nano::uint128_union const & other) const + { + return std::memcmp (bytes.data (), other.bytes.data (), 16) <=> 0; + }; + bool operator== (nano::uint128_union const & other) const + { + return *this <=> other == 0; + } + operator nano::uint128_t () const + { + return number (); + } + uint128_union const & as_union () const + { + return *this; + } }; static_assert (std::is_nothrow_move_constructible::value, "uint128_union should be noexcept MoveConstructible"); @@ -61,37 +106,69 @@ class amount : public uint128_union public: using uint128_union::uint128_union; + auto operator<=> (nano::amount const & other) const + { + return uint128_union::operator<=> (other); + } operator nano::uint128_t () const { return number (); } }; + class raw_key; class uint256_union { +public: + // Type that is implicitly convertible to this union + using underlying_type = nano::uint256_t; + public: uint256_union () = default; + uint256_union (uint64_t value) : + uint256_union (nano::uint256_t{ value }){}; + uint256_union (nano::uint256_t const & value) + { + bytes.fill (0); + boost::multiprecision::export_bits (value, bytes.rbegin (), 8, false); + } + /** * Decode from hex string * @warning Aborts at runtime if the input is invalid */ explicit uint256_union (std::string const &); - uint256_union (uint64_t); - uint256_union (nano::uint256_t const &); + void encrypt (nano::raw_key const &, nano::raw_key const &, uint128_union const &); - uint256_union & operator^= (nano::uint256_union const &); - uint256_union operator^ (nano::uint256_union const &) const; + + uint256_union & operator^= (uint256_union const &); + uint256_union operator^ (uint256_union const &) const; + void encode_hex (std::string &) const; bool decode_hex (std::string const &); void encode_dec (std::string &) const; bool decode_dec (std::string const &); - void clear (); - bool is_zero () const; + void clear () + { + qwords.fill (0); + } + bool is_zero () const + { + return owords[0].is_zero () && owords[1].is_zero (); + } + + nano::uint256_t number () const + { + nano::uint256_t result; + boost::multiprecision::import_bits (result, bytes.begin (), bytes.end ()); + return result; + } + std::string to_string () const; - nano::uint256_t number () const; +public: union { std::array bytes; @@ -100,33 +177,46 @@ class uint256_union std::array qwords; std::array owords; }; + +public: // Keep operators inlined + std::strong_ordering operator<=> (nano::uint256_union const & other) const + { + return std::memcmp (bytes.data (), other.bytes.data (), 32) <=> 0; + }; + bool operator== (nano::uint256_union const & other) const + { + return *this <=> other == 0; + } + operator nano::uint256_t () const + { + return number (); + } + uint256_union const & as_union () const + { + return *this; + } }; -inline bool operator== (nano::uint256_union const & lhs, nano::uint256_union const & rhs) -{ - return lhs.bytes == rhs.bytes; -} -inline bool operator!= (nano::uint256_union const & lhs, nano::uint256_union const & rhs) -{ - return !(lhs == rhs); -} -inline bool operator< (nano::uint256_union const & lhs, nano::uint256_union const & rhs) -{ - return std::memcmp (lhs.bytes.data (), rhs.bytes.data (), 32) < 0; -} static_assert (std::is_nothrow_move_constructible::value, "uint256_union should be noexcept MoveConstructible"); -class link; -class root; -class hash_or_account; - // All keys and hashes are 256 bit. class block_hash final : public uint256_union { public: using uint256_union::uint256_union; - operator nano::link const & () const; - operator nano::root const & () const; - operator nano::hash_or_account const & () const; + +public: // Keep operators inlined + auto operator<=> (nano::block_hash const & other) const + { + return uint256_union::operator<=> (other); + } + bool operator== (nano::block_hash const & other) const + { + return *this <=> other == 0; + } + operator nano::uint256_t () const + { + return number (); + } }; class public_key final : public uint256_union @@ -134,21 +224,35 @@ class public_key final : public uint256_union public: using uint256_union::uint256_union; - public_key (); + public_key () : + uint256_union{ 0 } {}; static const public_key & null (); - std::string to_node_id () const; - bool decode_node_id (std::string const & source_a); + bool decode_node_id (std::string const &); void encode_account (std::string &) const; - std::string to_account () const; bool decode_account (std::string const &); - operator nano::link const & () const; - operator nano::root const & () const; - operator nano::hash_or_account const & () const; - bool operator== (std::nullptr_t) const; - bool operator!= (std::nullptr_t) const; + std::string to_node_id () const; + std::string to_account () const; + +public: // Keep operators inlined + auto operator<=> (nano::public_key const & other) const + { + return uint256_union::operator<=> (other); + } + bool operator== (nano::public_key const & other) const + { + return *this <=> other == 0; + } + bool operator== (std::nullptr_t) const + { + return *this == null (); + } + operator nano::uint256_t () const + { + return number (); + } }; class wallet_id : public uint256_union @@ -162,24 +266,33 @@ using account = public_key; class hash_or_account { public: - hash_or_account (); - hash_or_account (uint64_t value_a); + // Type that is implicitly convertible to this union + using underlying_type = nano::uint256_t; + +public: + hash_or_account () : + account{} {}; + hash_or_account (uint64_t value) : + raw{ value } {}; + hash_or_account (uint256_union const & value) : + raw{ value } {}; + + void clear () + { + raw.clear (); + } + bool is_zero () const + { + return raw.is_zero (); + } - bool is_zero () const; - void clear (); - std::string to_string () const; bool decode_hex (std::string const &); bool decode_account (std::string const &); - std::string to_account () const; - - nano::account const & as_account () const; - nano::block_hash const & as_block_hash () const; - operator nano::uint256_union const & () const; - - bool operator== (nano::hash_or_account const &) const; - bool operator!= (nano::hash_or_account const &) const; + std::string to_string () const; + std::string to_account () const; +public: union { std::array bytes; @@ -187,6 +300,36 @@ class hash_or_account nano::account account; nano::block_hash hash; }; + +public: // Keep operators inlined + auto operator<=> (nano::hash_or_account const & other) const + { + return raw <=> other.raw; + }; + bool operator== (nano::hash_or_account const & other) const + { + return *this <=> other == 0; + } + explicit operator nano::uint256_t () const + { + return raw.number (); + } + explicit operator nano::uint256_union () const + { + return raw; + } + nano::account const & as_account () const + { + return account; + } + nano::block_hash const & as_block_hash () const + { + return hash; + } + nano::uint256_union const & as_union () const + { + return raw; + } }; // A link can either be a destination account or source hash @@ -194,6 +337,16 @@ class link final : public hash_or_account { public: using hash_or_account::hash_or_account; + +public: // Keep operators inlined + auto operator<=> (nano::link const & other) const + { + return hash_or_account::operator<=> (other); + } + bool operator== (nano::link const & other) const + { + return *this <=> other == 0; + } }; // A root can either be an open block hash or a previous hash @@ -202,7 +355,20 @@ class root final : public hash_or_account public: using hash_or_account::hash_or_account; - nano::block_hash const & previous () const; + nano::block_hash const & previous () const + { + return hash; + } + +public: // Keep operators inlined + auto operator<=> (nano::root const & other) const + { + return hash_or_account::operator<=> (other); + } + bool operator== (nano::root const & other) const + { + return *this <=> other == 0; + } }; // The seed or private key @@ -213,22 +379,52 @@ class raw_key final : public uint256_union ~raw_key (); void decrypt (nano::uint256_union const &, nano::raw_key const &, uint128_union const &); }; + class uint512_union { +public: + // Type that is implicitly convertible to this union + using underlying_type = nano::uint512_t; + public: uint512_union () = default; - uint512_union (nano::uint256_union const &, nano::uint256_union const &); - uint512_union (nano::uint512_t const &); - bool operator== (nano::uint512_union const &) const; - bool operator!= (nano::uint512_union const &) const; - nano::uint512_union & operator^= (nano::uint512_union const &); + uint512_union (nano::uint512_t const & value) + { + bytes.fill (0); + boost::multiprecision::export_bits (value, bytes.rbegin (), 8, false); + } + uint512_union (nano::uint256_union const & upper, nano::uint256_union const & lower) : + uint256s{ upper, lower } {}; + + nano::uint512_union & operator^= (nano::uint512_union const & other) + { + uint256s[0] ^= other.uint256s[0]; + uint256s[1] ^= other.uint256s[1]; + return *this; + } + void encode_hex (std::string &) const; bool decode_hex (std::string const &); - void clear (); - bool is_zero () const; - nano::uint512_t number () const; + + void clear () + { + bytes.fill (0); + } + bool is_zero () const + { + return uint256s[0].is_zero () && uint256s[1].is_zero (); + } + + nano::uint512_t number () const + { + nano::uint512_t result; + boost::multiprecision::import_bits (result, bytes.begin (), bytes.end ()); + return result; + } + std::string to_string () const; +public: union { std::array bytes; @@ -236,6 +432,24 @@ class uint512_union std::array qwords; std::array uint256s; }; + +public: // Keep operators inlined + std::strong_ordering operator<=> (nano::uint512_union const & other) const + { + return std::memcmp (bytes.data (), other.bytes.data (), 64) <=> 0; + }; + bool operator== (nano::uint512_union const & other) const + { + return *this <=> other == 0; + } + operator nano::uint512_t () const + { + return number (); + } + uint512_union const & as_union () const + { + return *this; + } }; static_assert (std::is_nothrow_move_constructible::value, "uint512_union should be noexcept MoveConstructible"); @@ -248,15 +462,19 @@ class signature : public uint512_union class qualified_root : public uint512_union { public: - using uint512_union::uint512_union; + qualified_root () = default; + qualified_root (nano::root const & root, nano::block_hash const & previous) : + uint512_union{ root.as_union (), previous.as_union () } {}; + qualified_root (nano::uint512_t const & value) : + uint512_union{ value } {}; - nano::root const & root () const + nano::root root () const { - return reinterpret_cast (uint256s[0]); + return nano::root{ uint256s[0] }; } - nano::block_hash const & previous () const + nano::block_hash previous () const { - return reinterpret_cast (uint256s[1]); + return nano::block_hash{ uint256s[1] }; } }; @@ -276,6 +494,7 @@ bool from_string_hex (std::string const &, uint64_t &); std::ostream & operator<< (std::ostream &, const uint128_union &); std::ostream & operator<< (std::ostream &, const uint256_union &); std::ostream & operator<< (std::ostream &, const uint512_union &); +std::ostream & operator<< (std::ostream &, const hash_or_account &); /** * Convert a double to string in fixed format @@ -297,113 +516,183 @@ namespace difficulty namespace std { template <> +struct hash<::nano::uint128_union> +{ + size_t operator() (::nano::uint128_union const & value) const noexcept + { + return value.qwords[0] + value.qwords[1]; + } +}; +template <> struct hash<::nano::uint256_union> { - size_t operator() (::nano::uint256_union const & data_a) const + size_t operator() (::nano::uint256_union const & value) const noexcept { - return data_a.qwords[0] + data_a.qwords[1] + data_a.qwords[2] + data_a.qwords[3]; + return value.qwords[0] + value.qwords[1] + value.qwords[2] + value.qwords[3]; } }; template <> -struct hash<::nano::account> +struct hash<::nano::public_key> { - size_t operator() (::nano::account const & data_a) const + size_t operator() (::nano::public_key const & value) const noexcept { - return hash<::nano::uint256_union> () (data_a); + return hash<::nano::uint256_union>{}(value); } }; template <> struct hash<::nano::block_hash> { - size_t operator() (::nano::block_hash const & data_a) const + size_t operator() (::nano::block_hash const & value) const noexcept { - return hash<::nano::uint256_union> () (data_a); + return hash<::nano::uint256_union>{}(value); } }; template <> struct hash<::nano::hash_or_account> { - size_t operator() (::nano::hash_or_account const & data_a) const + size_t operator() (::nano::hash_or_account const & value) const noexcept { - return hash<::nano::block_hash> () (data_a.as_block_hash ()); + return hash<::nano::block_hash>{}(value.as_block_hash ()); } }; template <> -struct hash<::nano::raw_key> +struct hash<::nano::root> { - size_t operator() (::nano::raw_key const & data_a) const + size_t operator() (::nano::root const & value) const noexcept { - return hash<::nano::uint256_union> () (data_a); + return hash<::nano::hash_or_account>{}(value); } }; template <> -struct hash<::nano::root> +struct hash<::nano::link> { - size_t operator() (::nano::root const & data_a) const + size_t operator() (::nano::link const & value) const noexcept { - return hash<::nano::uint256_union> () (data_a); + return hash<::nano::hash_or_account>{}(value); } }; template <> -struct hash<::nano::wallet_id> +struct hash<::nano::raw_key> { - size_t operator() (::nano::wallet_id const & data_a) const + size_t operator() (::nano::raw_key const & value) const noexcept { - return hash<::nano::uint256_union> () (data_a); + return hash<::nano::uint256_union>{}(value); } }; template <> -struct hash<::nano::uint256_t> +struct hash<::nano::wallet_id> { - size_t operator() (::nano::uint256_t const & number_a) const + size_t operator() (::nano::wallet_id const & value) const noexcept { - return number_a.convert_to (); + return hash<::nano::uint256_union>{}(value); } }; template <> struct hash<::nano::uint512_union> { - size_t operator() (::nano::uint512_union const & data_a) const + size_t operator() (::nano::uint512_union const & value) const noexcept { - return hash<::nano::uint256_union> () (data_a.uint256s[0]) + hash<::nano::uint256_union> () (data_a.uint256s[1]); + return hash<::nano::uint256_union>{}(value.uint256s[0]) + hash<::nano::uint256_union> () (value.uint256s[1]); } }; template <> struct hash<::nano::qualified_root> { - size_t operator() (::nano::qualified_root const & data_a) const + size_t operator() (::nano::qualified_root const & value) const noexcept { - return hash<::nano::uint512_union> () (data_a); + return hash<::nano::uint512_union>{}(value); } }; +} +namespace boost +{ template <> -struct equal_to> +struct hash<::nano::uint128_union> { - bool operator() (std::reference_wrapper<::nano::block_hash const> const & lhs, std::reference_wrapper<::nano::block_hash const> const & rhs) const + size_t operator() (::nano::uint128_union const & value) const noexcept { - return lhs.get () == rhs.get (); + return std::hash<::nano::uint128_union> () (value); } }; -} - -namespace boost +template <> +struct hash<::nano::uint256_union> { + size_t operator() (::nano::uint256_union const & value) const noexcept + { + return std::hash<::nano::uint256_union> () (value); + } +}; template <> -struct hash> +struct hash<::nano::public_key> { - size_t operator() (std::reference_wrapper<::nano::block_hash const> const & hash_a) const + size_t operator() (::nano::public_key const & value) const noexcept { - std::hash<::nano::block_hash> hash; - return hash (hash_a); + return std::hash<::nano::public_key> () (value); + } +}; +template <> +struct hash<::nano::block_hash> +{ + size_t operator() (::nano::block_hash const & value) const noexcept + { + return std::hash<::nano::block_hash> () (value); + } +}; +template <> +struct hash<::nano::hash_or_account> +{ + size_t operator() (::nano::hash_or_account const & value) const noexcept + { + return std::hash<::nano::hash_or_account> () (value); } }; template <> struct hash<::nano::root> { - size_t operator() (::nano::root const & value_a) const + size_t operator() (::nano::root const & value) const noexcept { - return std::hash<::nano::root> () (value_a); + return std::hash<::nano::root> () (value); + } +}; +template <> +struct hash<::nano::link> +{ + size_t operator() (::nano::link const & value) const noexcept + { + return std::hash<::nano::link> () (value); + } +}; +template <> +struct hash<::nano::raw_key> +{ + size_t operator() (::nano::raw_key const & value) const noexcept + { + return std::hash<::nano::raw_key> () (value); + } +}; +template <> +struct hash<::nano::wallet_id> +{ + size_t operator() (::nano::wallet_id const & value) const noexcept + { + return std::hash<::nano::wallet_id> () (value); + } +}; +template <> +struct hash<::nano::uint512_union> +{ + size_t operator() (::nano::uint512_union const & value) const noexcept + { + return std::hash<::nano::uint512_union> () (value); + } +}; +template <> +struct hash<::nano::qualified_root> +{ + size_t operator() (::nano::qualified_root const & value) const noexcept + { + return std::hash<::nano::qualified_root> () (value); } }; } @@ -427,6 +716,11 @@ struct fmt::formatter : fmt::ostream_formatter { }; +template <> +struct fmt::formatter : fmt::ostream_formatter +{ +}; + template <> struct fmt::formatter : fmt::formatter { @@ -440,4 +734,4 @@ struct fmt::formatter : fmt::formatter template <> struct fmt::formatter : fmt::formatter { -}; \ No newline at end of file +}; diff --git a/nano/lib/thread_pool.cpp b/nano/lib/thread_pool.cpp deleted file mode 100644 index c5f0108ee0..0000000000 --- a/nano/lib/thread_pool.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include - -#include -#include -#include - -/* - * thread_pool - */ - -nano::thread_pool::thread_pool (unsigned num_threads, nano::thread_role::name thread_name) : - num_threads (num_threads), - thread_pool_m (std::make_unique (num_threads)), - thread_names_latch{ num_threads } -{ - set_thread_names (thread_name); -} - -nano::thread_pool::~thread_pool () -{ - stop (); -} - -void nano::thread_pool::stop () -{ - nano::unique_lock lk (mutex); - if (!stopped) - { - stopped = true; -#if defined(BOOST_ASIO_HAS_IOCP) - // A hack needed for Windows to prevent deadlock during destruction, described here: https://github.com/chriskohlhoff/asio/issues/431 - boost::asio::use_service (*thread_pool_m).stop (); -#endif - lk.unlock (); - thread_pool_m->stop (); - thread_pool_m->join (); - lk.lock (); - thread_pool_m = nullptr; - } -} - -void nano::thread_pool::push_task (std::function task) -{ - ++num_tasks; - nano::lock_guard guard (mutex); - if (!stopped) - { - boost::asio::post (*thread_pool_m, [this, task] () { - task (); - --num_tasks; - }); - } -} - -void nano::thread_pool::add_timed_task (std::chrono::steady_clock::time_point const & expiry_time, std::function task) -{ - nano::lock_guard guard (mutex); - if (!stopped && thread_pool_m) - { - auto timer = std::make_shared (thread_pool_m->get_executor (), expiry_time); - timer->async_wait ([this, task, timer] (boost::system::error_code const & ec) { - if (!ec) - { - push_task (task); - } - }); - } -} - -unsigned nano::thread_pool::get_num_threads () const -{ - return num_threads; -} - -uint64_t nano::thread_pool::num_queued_tasks () const -{ - return num_tasks; -} - -void nano::thread_pool::set_thread_names (nano::thread_role::name thread_name) -{ - for (auto i = 0u; i < num_threads; ++i) - { - boost::asio::post (*thread_pool_m, [this, thread_name] () { - nano::thread_role::set (thread_name); - thread_names_latch.arrive_and_wait (); - }); - } - thread_names_latch.wait (); -} - -nano::container_info nano::thread_pool::container_info () const -{ - nano::container_info info; - info.put ("count", num_queued_tasks ()); - return info; -} diff --git a/nano/lib/thread_pool.hpp b/nano/lib/thread_pool.hpp index 2b32e60f59..157272b7fe 100644 --- a/nano/lib/thread_pool.hpp +++ b/nano/lib/thread_pool.hpp @@ -4,50 +4,151 @@ #include #include +#include +#include +#include + #include #include -#include #include - -namespace boost::asio -{ -class thread_pool; -} +#include +#include namespace nano { class thread_pool final { public: - explicit thread_pool (unsigned num_threads, nano::thread_role::name); - ~thread_pool (); + // TODO: Auto start should be removed once the node is refactored to start the thread pool explicitly + thread_pool (unsigned num_threads, nano::thread_role::name thread_name, bool auto_start = false) : + num_threads{ num_threads }, + thread_name{ thread_name }, + thread_names_latch{ num_threads } + { + if (auto_start) + { + start (); + } + } + + ~thread_pool () + { + // Must be stopped before destruction to avoid running takss when node components are being destroyed + debug_assert (!thread_pool_impl); + } + + void start () + { + debug_assert (!stopped); + debug_assert (!thread_pool_impl); + thread_pool_impl = std::make_unique (num_threads); + set_thread_names (); + } + + void stop () + { + nano::unique_lock lock{ mutex }; + if (!stopped && thread_pool_impl) + { + stopped = true; + + // TODO: Is this still needed? +#if defined(BOOST_ASIO_HAS_IOCP) + // A hack needed for Windows to prevent deadlock during destruction, described here: https://github.com/chriskohlhoff/asio/issues/431 + boost::asio::use_service (*thread_pool_impl).stop (); +#endif - /** This will run when there is an available thread for execution */ - void push_task (std::function); + lock.unlock (); - /** Run a task at a certain point in time */ - void add_timed_task (std::chrono::steady_clock::time_point const & expiry_time, std::function task); + thread_pool_impl->stop (); + thread_pool_impl->join (); - /** Stops any further pushed tasks from executing */ - void stop (); + lock.lock (); + thread_pool_impl = nullptr; + } + } - /** Number of threads in the thread pool */ - unsigned get_num_threads () const; + template + void post (F && task) + { + nano::lock_guard guard{ mutex }; + if (!stopped) + { + ++num_tasks; + release_assert (thread_pool_impl); + boost::asio::post (*thread_pool_impl, [this, t = std::forward (task)] () mutable { + t (); + --num_tasks; + }); + } + } - /** Returns the number of tasks which are awaiting execution by the thread pool **/ - uint64_t num_queued_tasks () const; + template + void post_delayed (std::chrono::steady_clock::duration const & delay, F && task) + { + nano::lock_guard guard{ mutex }; + if (!stopped) + { + ++num_delayed; + release_assert (thread_pool_impl); + auto timer = std::make_shared (thread_pool_impl->get_executor ()); + timer->expires_after (delay); + timer->async_wait ([this, t = std::forward (task), /* preserve lifetime */ timer] (boost::system::error_code const & ec) mutable { + if (!ec) + { + --num_delayed; + post (std::move (t)); + } + }); + } + } - nano::container_info container_info () const; + bool alive () const + { + nano::lock_guard guard{ mutex }; + return thread_pool_impl != nullptr; + } + + uint64_t queued_tasks () const + { + return num_tasks; + } + + uint64_t delayed_tasks () const + { + return num_delayed; + } + + nano::container_info container_info () const + { + nano::container_info info; + info.put ("tasks", num_tasks); + info.put ("delayed", num_delayed); + return info; + } private: - nano::mutex mutex; - std::atomic stopped{ false }; - unsigned num_threads; - std::unique_ptr thread_pool_m; - nano::relaxed_atomic_integral num_tasks{ 0 }; + void set_thread_names () + { + for (auto i = 0u; i < num_threads; ++i) + { + boost::asio::post (*thread_pool_impl, [this] () { + nano::thread_role::set (thread_name); + thread_names_latch.arrive_and_wait (); + }); + } + thread_names_latch.wait (); + } + +private: + unsigned const num_threads; + nano::thread_role::name const thread_name; - /** Set the names of all the threads in the thread pool for easier identification */ std::latch thread_names_latch; - void set_thread_names (nano::thread_role::name thread_name); + mutable nano::mutex mutex; + std::atomic stopped{ false }; + std::unique_ptr thread_pool_impl; + std::atomic num_tasks{ 0 }; + std::atomic num_delayed{ 0 }; }; -} // namespace nano +} \ No newline at end of file diff --git a/nano/node/bootstrap/bootstrap.cpp b/nano/node/bootstrap/bootstrap.cpp index 3db711dfce..8779400eec 100644 --- a/nano/node/bootstrap/bootstrap.cpp +++ b/nano/node/bootstrap/bootstrap.cpp @@ -313,7 +313,7 @@ void nano::pulls_cache::add (nano::pull_info const & pull_a) cache.erase (cache.begin ()); } debug_assert (cache.size () <= cache_size_max); - nano::uint512_union head_512 (pull_a.account_or_head, pull_a.head_original); + nano::uint512_union head_512 (pull_a.account_or_head.as_union (), pull_a.head_original); auto existing (cache.get ().find (head_512)); if (existing == cache.get ().end ()) { @@ -336,7 +336,7 @@ void nano::pulls_cache::add (nano::pull_info const & pull_a) void nano::pulls_cache::update_pull (nano::pull_info & pull_a) { nano::lock_guard guard{ pulls_cache_mutex }; - nano::uint512_union head_512 (pull_a.account_or_head, pull_a.head_original); + nano::uint512_union head_512 (pull_a.account_or_head.as_union (), pull_a.head_original); auto existing (cache.get ().find (head_512)); if (existing != cache.get ().end ()) { @@ -347,7 +347,7 @@ void nano::pulls_cache::update_pull (nano::pull_info & pull_a) void nano::pulls_cache::remove (nano::pull_info const & pull_a) { nano::lock_guard guard{ pulls_cache_mutex }; - nano::uint512_union head_512 (pull_a.account_or_head, pull_a.head_original); + nano::uint512_union head_512 (pull_a.account_or_head.as_union (), pull_a.head_original); cache.get ().erase (head_512); } diff --git a/nano/node/bootstrap/bootstrap_bulk_pull.cpp b/nano/node/bootstrap/bootstrap_bulk_pull.cpp index a4967bf35c..f17b08f939 100644 --- a/nano/node/bootstrap/bootstrap_bulk_pull.cpp +++ b/nano/node/bootstrap/bootstrap_bulk_pull.cpp @@ -127,7 +127,7 @@ void nano::bulk_pull_client::throttled_receive_block () else { auto this_l (shared_from_this ()); - node->workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (1), [this_l] () { + node->workers.post_delayed (std::chrono::seconds (1), [this_l] () { if (!this_l->connection->pending_stop && !this_l->attempt->stopped) { this_l->throttled_receive_block (); @@ -177,7 +177,7 @@ void nano::bulk_pull_client::received_block (boost::system::error_code ec, std:: // Is block expected? bool block_expected (false); // Unconfirmed head is used only for lazy destinations if legacy bootstrap is not available, see nano::bootstrap_attempt::lazy_destinations_increment (...) - bool unconfirmed_account_head = node->flags.disable_legacy_bootstrap && pull_blocks == 0 && pull.retry_limit <= node->network_params.bootstrap.lazy_retry_limit && (expected == pull.account_or_head.as_block_hash ()) && (block->account_field () == pull.account_or_head.as_account ()); + bool unconfirmed_account_head = node->flags.disable_legacy_bootstrap && pull_blocks == 0 && pull.retry_limit <= node->network_params.bootstrap.lazy_retry_limit && (expected == pull.account_or_head.as_block_hash ()) && (block->account_field ().value_or (0) == pull.account_or_head.as_account ()); if (hash == expected || unconfirmed_account_head) { expected = block->previous (); @@ -394,7 +394,7 @@ void nano::bulk_pull_server::set_current_end () if (!request->end.is_zero ()) { auto account (node->ledger.any.block_account (transaction, request->end)); - if (account != request->start.as_account ()) + if (account.value_or (0) != request->start.as_account ()) { node->logger.debug (nano::log::type::bulk_pull_server, "Request for block that is not on account chain: {} not on {}", request->end.to_string (), request->start.to_account ()); @@ -530,7 +530,7 @@ void nano::bulk_pull_server::sent_action (boost::system::error_code const & ec, } if (!ec) { - node->bootstrap_workers.push_task ([this_l = shared_from_this ()] () { + node->bootstrap_workers.post ([this_l = shared_from_this ()] () { this_l->send_next (); }); } @@ -816,7 +816,7 @@ void nano::bulk_pull_account_server::sent_action (boost::system::error_code cons } if (!ec) { - node->bootstrap_workers.push_task ([this_l = shared_from_this ()] () { + node->bootstrap_workers.post ([this_l = shared_from_this ()] () { this_l->send_next_block (); }); } diff --git a/nano/node/bootstrap/bootstrap_bulk_push.cpp b/nano/node/bootstrap/bootstrap_bulk_push.cpp index 47508878a3..c023ec1fe5 100644 --- a/nano/node/bootstrap/bootstrap_bulk_push.cpp +++ b/nano/node/bootstrap/bootstrap_bulk_push.cpp @@ -144,7 +144,7 @@ void nano::bulk_push_server::throttled_receive () else { auto this_l (shared_from_this ()); - node->workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (1), [this_l] () { + node->workers.post_delayed (std::chrono::seconds (1), [this_l] () { if (!this_l->connection->stopped) { this_l->throttled_receive (); diff --git a/nano/node/bootstrap/bootstrap_connections.cpp b/nano/node/bootstrap/bootstrap_connections.cpp index 358538a651..b3f1334b8f 100644 --- a/nano/node/bootstrap/bootstrap_connections.cpp +++ b/nano/node/bootstrap/bootstrap_connections.cpp @@ -306,7 +306,7 @@ void nano::bootstrap_connections::populate_connections (bool repeat) if (!stopped && repeat) { std::weak_ptr this_w (shared_from_this ()); - node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (1), [this_w] () { + node.workers.post_delayed (std::chrono::seconds (1), [this_w] () { if (auto this_l = this_w.lock ()) { this_l->populate_connections (); diff --git a/nano/node/bootstrap/bootstrap_frontier.cpp b/nano/node/bootstrap/bootstrap_frontier.cpp index db696dbd61..8a6067ef2c 100644 --- a/nano/node/bootstrap/bootstrap_frontier.cpp +++ b/nano/node/bootstrap/bootstrap_frontier.cpp @@ -22,7 +22,7 @@ void nano::frontier_req_client::run (nano::account const & start_account_a, uint return; } nano::frontier_req request{ node->network_params.network }; - request.start = (start_account_a.is_zero () || start_account_a.number () == std::numeric_limits::max ()) ? start_account_a : start_account_a.number () + 1; + request.start = (start_account_a.is_zero () || start_account_a.number () == std::numeric_limits::max ()) ? start_account_a.number () : start_account_a.number () + 1; request.age = frontiers_age_a; request.count = count_a; current = start_account_a; @@ -70,7 +70,7 @@ void nano::frontier_req_client::receive_frontier () // we simply get a size of 0. if (size_a == nano::frontier_req_client::size_frontier) { - node->bootstrap_workers.push_task ([this_l, ec, size_a] () { + node->bootstrap_workers.post ([this_l, ec, size_a] () { this_l->received_frontier (ec, size_a); }); } @@ -355,7 +355,7 @@ void nano::frontier_req_server::sent_action (boost::system::error_code const & e { count++; - node->bootstrap_workers.push_task ([this_l = shared_from_this ()] () { + node->bootstrap_workers.post ([this_l = shared_from_this ()] () { this_l->send_next (); }); } diff --git a/nano/node/bootstrap/bootstrap_lazy.cpp b/nano/node/bootstrap/bootstrap_lazy.cpp index 30661abd7a..439aa0ced0 100644 --- a/nano/node/bootstrap/bootstrap_lazy.cpp +++ b/nano/node/bootstrap/bootstrap_lazy.cpp @@ -293,7 +293,7 @@ bool nano::bootstrap_attempt_lazy::process_block_lazy (std::shared_ptrsource_field () && !node->block_or_pruned_exists (block_a->source_field ().value ()) && block_a->source_field ().value () != node->network_params.ledger.genesis->account ()) + if (block_a->source_field () && !node->block_or_pruned_exists (block_a->source_field ().value ()) && block_a->source_field ().value () != node->network_params.ledger.genesis->account ().as_union ()) { lazy_add (block_a->source_field ().value (), retry_limit); } diff --git a/nano/node/bootstrap_ascending/service.cpp b/nano/node/bootstrap_ascending/service.cpp index 776f0b3d8e..661072b448 100644 --- a/nano/node/bootstrap_ascending/service.cpp +++ b/nano/node/bootstrap_ascending/service.cpp @@ -789,7 +789,7 @@ nano::bootstrap_ascending::service::verify_result nano::bootstrap_ascending::ser case query_type::blocks_by_account: { // Open & state blocks always contain account field - if (first->account_field () != tag.start.as_account ()) + if (first->account_field ().value_or (0) != tag.start.as_account ()) { // TODO: Stat & log return verify_result::invalid; diff --git a/nano/node/confirming_set.cpp b/nano/node/confirming_set.cpp index 4d8d145084..e504b0acae 100644 --- a/nano/node/confirming_set.cpp +++ b/nano/node/confirming_set.cpp @@ -55,6 +55,8 @@ void nano::confirming_set::start () return; } + notification_workers.start (); + thread = std::thread{ [this] () { nano::thread_role::set (nano::thread_role::name::confirmation_height); run (); @@ -148,7 +150,7 @@ void nano::confirming_set::run_batch (std::unique_lock & lock) std::unique_lock lock{ mutex }; // It's possible that ledger cementing happens faster than the notifications can be processed by other components, cooldown here - while (notification_workers.num_queued_tasks () >= config.max_queued_notifications) + while (notification_workers.queued_tasks () >= config.max_queued_notifications) { stats.inc (nano::stat::type::confirming_set, nano::stat::detail::cooldown); condition.wait_for (lock, 100ms, [this] { return stopped.load (); }); @@ -158,7 +160,7 @@ void nano::confirming_set::run_batch (std::unique_lock & lock) } } - notification_workers.push_task ([this, batch = std::move (batch)] () { + notification_workers.post ([this, batch = std::move (batch)] () { stats.inc (nano::stat::type::confirming_set, nano::stat::detail::notify); batch_cemented.notify (batch); }); diff --git a/nano/node/distributed_work.cpp b/nano/node/distributed_work.cpp index 1404c06526..81d771a5e1 100644 --- a/nano/node/distributed_work.cpp +++ b/nano/node/distributed_work.cpp @@ -401,10 +401,9 @@ void nano::distributed_work::handle_failure () status = work_generation_status::failure_peers; - auto now (std::chrono::steady_clock::now ()); std::weak_ptr node_weak (node.shared ()); auto next_backoff (std::min (backoff * 2, std::chrono::seconds (5 * 60))); - node.workers.add_timed_task (now + std::chrono::seconds (backoff), [node_weak, request_l = request, next_backoff] { + node.workers.post_delayed (std::chrono::seconds (backoff), [node_weak, request_l = request, next_backoff] { bool error_l{ true }; if (auto node_l = node_weak.lock ()) { diff --git a/nano/node/election.cpp b/nano/node/election.cpp index 93b9e8f7dc..06ea41350d 100644 --- a/nano/node/election.cpp +++ b/nano/node/election.cpp @@ -62,7 +62,7 @@ void nano::election::confirm_once (nano::unique_lock & lock) lock.unlock (); - node.election_workers.push_task ([this_l = shared_from_this (), status_l, confirmation_action_l = confirmation_action] () { + node.election_workers.post ([this_l = shared_from_this (), status_l, confirmation_action_l = confirmation_action] () { // This is necessary if the winner of the election is one of the forks. // In that case the winning block is not yet in the ledger and cementing needs to wait for rollbacks to complete. this_l->node.process_confirmed (status_l.winner->hash (), this_l); diff --git a/nano/node/epoch_upgrader.cpp b/nano/node/epoch_upgrader.cpp index 6667e7e20d..69d2a50657 100644 --- a/nano/node/epoch_upgrader.cpp +++ b/nano/node/epoch_upgrader.cpp @@ -161,7 +161,7 @@ void nano::epoch_upgrader::upgrade_impl (nano::raw_key const & prv_a, nano::epoc upgrader_condition.wait (lock); } } - node.workers.push_task ([&upgrader_process, &upgrader_mutex, &upgrader_condition, &upgraded_accounts, &workers, epoch, difficulty, signer, root, account] () { + node.workers.post ([&upgrader_process, &upgrader_mutex, &upgrader_condition, &upgraded_accounts, &workers, epoch, difficulty, signer, root, account] () { upgrader_process (upgraded_accounts, epoch, difficulty, signer, root, account); { nano::lock_guard lock{ upgrader_mutex }; @@ -241,7 +241,7 @@ void nano::epoch_upgrader::upgrade_impl (nano::raw_key const & prv_a, nano::epoc upgrader_condition.wait (lock); } } - node.workers.push_task ([&upgrader_process, &upgrader_mutex, &upgrader_condition, &upgraded_pending, &workers, epoch, difficulty, signer, root, account] () { + node.workers.post ([&upgrader_process, &upgrader_mutex, &upgrader_condition, &upgraded_pending, &workers, epoch, difficulty, signer, root, account] () { upgrader_process (upgraded_pending, epoch, difficulty, signer, root, account); { nano::lock_guard lock{ upgrader_mutex }; diff --git a/nano/node/fwd.hpp b/nano/node/fwd.hpp index 57b2169655..7bdcb57665 100644 --- a/nano/node/fwd.hpp +++ b/nano/node/fwd.hpp @@ -9,6 +9,7 @@ namespace nano { class block; class container_info; +class thread_pool; } namespace nano diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index 591ce4b997..288296d052 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -541,7 +541,7 @@ void nano::json_handler::account_block_count () void nano::json_handler::account_create () { - node.workers.push_task (create_worker_task ([] (std::shared_ptr const & rpc_l) { + node.workers.post (create_worker_task ([] (std::shared_ptr const & rpc_l) { auto wallet (rpc_l->wallet_impl ()); if (!rpc_l->ec) { @@ -731,7 +731,7 @@ void nano::json_handler::account_list () void nano::json_handler::account_move () { - node.workers.push_task (create_worker_task ([] (std::shared_ptr const & rpc_l) { + node.workers.post (create_worker_task ([] (std::shared_ptr const & rpc_l) { auto wallet (rpc_l->wallet_impl ()); if (!rpc_l->ec) { @@ -770,7 +770,7 @@ void nano::json_handler::account_move () void nano::json_handler::account_remove () { - node.workers.push_task (create_worker_task ([] (std::shared_ptr const & rpc_l) { + node.workers.post (create_worker_task ([] (std::shared_ptr const & rpc_l) { auto wallet (rpc_l->wallet_impl ()); auto account (rpc_l->account_impl ()); if (!rpc_l->ec) @@ -805,7 +805,7 @@ void nano::json_handler::account_representative () void nano::json_handler::account_representative_set () { - node.workers.push_task (create_worker_task ([work_generation_enabled = node.work_generation_enabled ()] (std::shared_ptr const & rpc_l) { + node.workers.post (create_worker_task ([work_generation_enabled = node.work_generation_enabled ()] (std::shared_ptr const & rpc_l) { auto wallet (rpc_l->wallet_impl ()); auto account (rpc_l->account_impl ()); std::string representative_text (rpc_l->request.get ("representative")); @@ -948,7 +948,7 @@ void nano::json_handler::accounts_representatives () void nano::json_handler::accounts_create () { - node.workers.push_task (create_worker_task ([] (std::shared_ptr const & rpc_l) { + node.workers.post (create_worker_task ([] (std::shared_ptr const & rpc_l) { auto wallet (rpc_l->wallet_impl ()); auto count (rpc_l->count_impl ()); if (!rpc_l->ec) @@ -2462,7 +2462,7 @@ class history_visitor : public nano::block_visitor // Report opens as a receive tree.put ("type", "receive"); } - if (block_a.hashables.source != handler.node.ledger.constants.genesis->account ()) + if (block_a.hashables.source != handler.node.ledger.constants.genesis->account ().as_union ()) { bool error_or_pruned (false); auto amount = handler.node.ledger.any.block_amount (transaction, hash); @@ -2930,7 +2930,7 @@ void nano::json_handler::node_id_delete () void nano::json_handler::password_change () { - node.workers.push_task (create_worker_task ([] (std::shared_ptr const & rpc_l) { + node.workers.post (create_worker_task ([] (std::shared_ptr const & rpc_l) { auto wallet (rpc_l->wallet_impl ()); if (!rpc_l->ec) { @@ -2953,7 +2953,7 @@ void nano::json_handler::password_change () void nano::json_handler::password_enter () { - node.workers.push_task (create_worker_task ([] (std::shared_ptr const & rpc_l) { + node.workers.post (create_worker_task ([] (std::shared_ptr const & rpc_l) { auto wallet (rpc_l->wallet_impl ()); if (!rpc_l->ec) { @@ -3178,7 +3178,7 @@ void nano::json_handler::receivable_exists () void nano::json_handler::process () { - node.workers.push_task (create_worker_task ([] (std::shared_ptr const & rpc_l) { + node.workers.post (create_worker_task ([] (std::shared_ptr const & rpc_l) { bool const is_async = rpc_l->request.get ("async", false); auto block (rpc_l->block_impl (true)); @@ -4143,7 +4143,7 @@ void nano::json_handler::unchecked () void nano::json_handler::unchecked_clear () { - node.workers.push_task (create_worker_task ([] (std::shared_ptr const & rpc_l) { + node.workers.post (create_worker_task ([] (std::shared_ptr const & rpc_l) { rpc_l->node.unchecked.clear (); rpc_l->response_l.put ("success", ""); rpc_l->response_errors (); @@ -4316,7 +4316,7 @@ void nano::json_handler::validate_account_number () void nano::json_handler::wallet_add () { - node.workers.push_task (create_worker_task ([] (std::shared_ptr const & rpc_l) { + node.workers.post (create_worker_task ([] (std::shared_ptr const & rpc_l) { auto wallet (rpc_l->wallet_impl ()); if (!rpc_l->ec) { @@ -4346,7 +4346,7 @@ void nano::json_handler::wallet_add () void nano::json_handler::wallet_add_watch () { - node.workers.push_task (create_worker_task ([] (std::shared_ptr const & rpc_l) { + node.workers.post (create_worker_task ([] (std::shared_ptr const & rpc_l) { auto wallet (rpc_l->wallet_impl ()); if (!rpc_l->ec) { @@ -4469,7 +4469,7 @@ void nano::json_handler::wallet_balances () void nano::json_handler::wallet_change_seed () { - node.workers.push_task (create_worker_task ([] (std::shared_ptr const & rpc_l) { + node.workers.post (create_worker_task ([] (std::shared_ptr const & rpc_l) { auto wallet (rpc_l->wallet_impl ()); if (!rpc_l->ec) { @@ -4517,7 +4517,7 @@ void nano::json_handler::wallet_contains () void nano::json_handler::wallet_create () { - node.workers.push_task (create_worker_task ([] (std::shared_ptr const & rpc_l) { + node.workers.post (create_worker_task ([] (std::shared_ptr const & rpc_l) { nano::raw_key seed; auto seed_text (rpc_l->request.get_optional ("seed")); if (seed_text.is_initialized () && seed.decode_hex (seed_text.get ())) @@ -4553,7 +4553,7 @@ void nano::json_handler::wallet_create () void nano::json_handler::wallet_destroy () { - node.workers.push_task (create_worker_task ([] (std::shared_ptr const & rpc_l) { + node.workers.post (create_worker_task ([] (std::shared_ptr const & rpc_l) { std::string wallet_text (rpc_l->request.get ("wallet")); nano::wallet_id wallet; if (!wallet.decode_hex (wallet_text)) @@ -4845,7 +4845,7 @@ void nano::json_handler::wallet_representative () void nano::json_handler::wallet_representative_set () { - node.workers.push_task (create_worker_task ([] (std::shared_ptr const & rpc_l) { + node.workers.post (create_worker_task ([] (std::shared_ptr const & rpc_l) { auto wallet (rpc_l->wallet_impl ()); std::string representative_text (rpc_l->request.get ("representative")); auto representative (rpc_l->account_impl (representative_text, nano::error_rpc::bad_representative_number)); @@ -5132,7 +5132,7 @@ void nano::json_handler::work_get () void nano::json_handler::work_set () { - node.workers.push_task (create_worker_task ([] (std::shared_ptr const & rpc_l) { + node.workers.post (create_worker_task ([] (std::shared_ptr const & rpc_l) { auto wallet (rpc_l->wallet_impl ()); auto account (rpc_l->account_impl ()); auto work (rpc_l->work_optional_impl ()); diff --git a/nano/node/network.cpp b/nano/node/network.cpp index 269edcd87f..b8f95f471c 100644 --- a/nano/node/network.cpp +++ b/nano/node/network.cpp @@ -309,7 +309,7 @@ void nano::network::flood_block_many (std::deque> b if (!blocks_a.empty ()) { std::weak_ptr node_w (node.shared ()); - node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::milliseconds (delay_a + std::rand () % delay_a), [node_w, blocks (std::move (blocks_a)), callback_a, delay_a] () { + node.workers.post_delayed (std::chrono::milliseconds (delay_a + std::rand () % delay_a), [node_w, blocks (std::move (blocks_a)), callback_a, delay_a] () { if (auto node_l = node_w.lock ()) { node_l->network.flood_block_many (std::move (blocks), callback_a, delay_a); diff --git a/nano/node/node.cpp b/nano/node/node.cpp index b3d5955511..42985173d5 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -69,6 +70,7 @@ nano::node::node (std::shared_ptr io_ctx_a, uint16_t pe nano::node::node (std::shared_ptr io_ctx_a, std::filesystem::path const & application_path_a, nano::node_config const & config_a, nano::work_pool & work_a, nano::node_flags flags_a, unsigned seq) : node_id{ load_or_create_node_id (application_path_a) }, config{ config_a }, + flags{ flags_a }, io_ctx_shared{ std::make_shared () }, io_ctx{ *io_ctx_shared }, logger{ make_logger_identifier (node_id) }, @@ -77,11 +79,14 @@ nano::node::node (std::shared_ptr io_ctx_a, std::filesy node_initialized_latch (1), network_params{ config.network_params }, stats{ logger, config.stats_config }, - workers{ config.background_threads, nano::thread_role::name::worker }, - bootstrap_workers{ config.bootstrap_serving_threads, nano::thread_role::name::bootstrap_worker }, - wallet_workers{ 1, nano::thread_role::name::wallet_worker }, - election_workers{ 1, nano::thread_role::name::election_worker }, - flags (flags_a), + workers_impl{ std::make_unique (config.background_threads, nano::thread_role::name::worker, /* start immediately */ true) }, + workers{ *workers_impl }, + bootstrap_workers_impl{ std::make_unique (config.bootstrap_serving_threads, nano::thread_role::name::bootstrap_worker, /* start immediately */ true) }, + bootstrap_workers{ *bootstrap_workers_impl }, + wallet_workers_impl{ std::make_unique (1, nano::thread_role::name::wallet_worker, /* start immediately */ true) }, + wallet_workers{ *wallet_workers_impl }, + election_workers_impl{ std::make_unique (1, nano::thread_role::name::election_worker, /* start immediately */ true) }, + election_workers{ *election_workers_impl }, work (work_a), distributed_work (*this), store_impl (nano::make_store (logger, application_path_a, network_params.ledger, flags.read_only, true, config_a.rocksdb_config, config_a.diagnostics_config.txn_tracking, config_a.block_processor_batch_max_time, config_a.lmdb_config, config_a.backup_before_upgrade)), @@ -402,7 +407,7 @@ nano::node::node (std::shared_ptr io_ctx_a, std::filesy // TODO: Is it neccessary to call this for all blocks? if (block->is_send ()) { - wallet_workers.push_task ([this, hash = block->hash (), destination = block->destination ()] () { + wallet_workers.post ([this, hash = block->hash (), destination = block->destination ()] () { wallets.receive_confirmed (hash, destination); }); } @@ -564,7 +569,7 @@ void nano::node::start () if (flags.enable_pruning) { auto this_l (shared ()); - workers.push_task ([this_l] () { + workers.post ([this_l] () { this_l->ongoing_ledger_pruning (); }); } @@ -605,7 +610,7 @@ void nano::node::start () { // Delay to start wallet lazy bootstrap auto this_l (shared ()); - workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::minutes (1), [this_l] () { + workers.post_delayed (std::chrono::minutes (1), [this_l] () { this_l->bootstrap_wallet (); }); } @@ -654,9 +659,7 @@ void nano::node::stop () logger.info (nano::log::type::node, "Node stopping..."); tcp_listener.stop (); - bootstrap_workers.stop (); - wallet_workers.stop (); - election_workers.stop (); + vote_router.stop (); peer_history.stop (); // Cancels ongoing work generation tasks, which may be blocking other threads @@ -684,12 +687,16 @@ void nano::node::stop () wallets.stop (); stats.stop (); epoch_upgrader.stop (); - workers.stop (); local_block_broadcaster.stop (); message_processor.stop (); network.stop (); // Stop network last to avoid killing in-use sockets monitor.stop (); + bootstrap_workers.stop (); + wallet_workers.stop (); + election_workers.stop (); + workers.stop (); + // work pool is not stopped on purpose due to testing setup // Stop the IO runner last @@ -755,7 +762,7 @@ void nano::node::long_inactivity_cleanup () if (store.online_weight.count (transaction) > 0) { auto sample (store.online_weight.rbegin (transaction)); - auto n (store.online_weight.end (transaction)); + auto n (store.online_weight.rend (transaction)); debug_assert (sample != n); auto const one_week_ago = static_cast ((std::chrono::system_clock::now () - std::chrono::hours (7 * 24)).time_since_epoch ().count ()); perform_cleanup = sample->first < one_week_ago; @@ -823,7 +830,7 @@ void nano::node::ongoing_bootstrap () // Bootstrap and schedule for next attempt bootstrap_initiator.bootstrap (false, boost::str (boost::format ("auto_bootstrap_%1%") % previous_bootstrap_count), frontiers_age); std::weak_ptr node_w (shared_from_this ()); - workers.add_timed_task (std::chrono::steady_clock::now () + next_wakeup, [node_w] () { + workers.post_delayed (next_wakeup, [node_w] () { if (auto node_l = node_w.lock ()) { node_l->ongoing_bootstrap (); @@ -844,7 +851,7 @@ void nano::node::backup_wallet () i->second->store.write_backup (transaction, backup_path / (i->first.to_string () + ".json")); } auto this_l (shared ()); - workers.add_timed_task (std::chrono::steady_clock::now () + network_params.node.backup_interval, [this_l] () { + workers.post_delayed (network_params.node.backup_interval, [this_l] () { this_l->backup_wallet (); }); } @@ -856,7 +863,7 @@ void nano::node::search_receivable_all () // Search pending wallets.search_receivable_all (); auto this_l (shared ()); - workers.add_timed_task (std::chrono::steady_clock::now () + network_params.node.search_pending_interval, [this_l] () { + workers.post_delayed (network_params.node.search_pending_interval, [this_l] () { this_l->search_receivable_all (); }); } @@ -981,8 +988,8 @@ void nano::node::ongoing_ledger_pruning () ledger_pruning (flags.block_processor_batch_size != 0 ? flags.block_processor_batch_size : 2 * 1024, bootstrap_weight_reached); auto const ledger_pruning_interval (bootstrap_weight_reached ? config.max_pruning_age : std::min (config.max_pruning_age, std::chrono::seconds (15 * 60))); auto this_l (shared ()); - workers.add_timed_task (std::chrono::steady_clock::now () + ledger_pruning_interval, [this_l] () { - this_l->workers.push_task ([this_l] () { + workers.post_delayed (ledger_pruning_interval, [this_l] () { + this_l->workers.post ([this_l] () { this_l->ongoing_ledger_pruning (); }); }); @@ -1126,7 +1133,7 @@ bool nano::node::block_confirmed_or_being_confirmed (nano::block_hash const & ha void nano::node::ongoing_online_weight_calculation_queue () { std::weak_ptr node_w (shared_from_this ()); - workers.add_timed_task (std::chrono::steady_clock::now () + (std::chrono::seconds (network_params.node.weight_period)), [node_w] () { + workers.post_delayed ((std::chrono::seconds (network_params.node.weight_period)), [node_w] () { if (auto node_l = node_w.lock ()) { node_l->ongoing_online_weight_calculation (); @@ -1165,7 +1172,7 @@ void nano::node::process_confirmed (nano::block_hash hash, std::shared_ptr #include #include -#include #include #include #include @@ -148,6 +147,7 @@ class node final : public std::enable_shared_from_this public: const nano::keypair node_id; nano::node_config config; + nano::node_flags flags; std::shared_ptr io_ctx_shared; boost::asio::io_context & io_ctx; nano::logger logger; @@ -156,11 +156,14 @@ class node final : public std::enable_shared_from_this boost::latch node_initialized_latch; nano::network_params & network_params; nano::stats stats; - nano::thread_pool workers; - nano::thread_pool bootstrap_workers; - nano::thread_pool wallet_workers; - nano::thread_pool election_workers; - nano::node_flags flags; + std::unique_ptr workers_impl; + nano::thread_pool & workers; + std::unique_ptr bootstrap_workers_impl; + nano::thread_pool & bootstrap_workers; + std::unique_ptr wallet_workers_impl; + nano::thread_pool & wallet_workers; + std::unique_ptr election_workers_impl; + nano::thread_pool & election_workers; nano::work_pool & work; nano::distributed_work_factory distributed_work; std::unique_ptr store_impl; diff --git a/nano/node/transport/tcp_server.cpp b/nano/node/transport/tcp_server.cpp index a4e59e6748..881da00c50 100644 --- a/nano/node/transport/tcp_server.cpp +++ b/nano/node/transport/tcp_server.cpp @@ -526,7 +526,7 @@ void nano::transport::tcp_server::bootstrap_message_visitor::bulk_pull (const na return; } - node->bootstrap_workers.push_task ([server = server, message = message] () { + node->bootstrap_workers.post ([server = server, message = message] () { // TODO: Add completion callback to bulk pull server // TODO: There should be no need to re-copy message as unique pointer, refactor those bulk/frontier pull/push servers auto bulk_pull_server = std::make_shared (server, std::make_unique (message)); @@ -548,7 +548,7 @@ void nano::transport::tcp_server::bootstrap_message_visitor::bulk_pull_account ( return; } - node->bootstrap_workers.push_task ([server = server, message = message] () { + node->bootstrap_workers.post ([server = server, message = message] () { // TODO: Add completion callback to bulk pull server // TODO: There should be no need to re-copy message as unique pointer, refactor those bulk/frontier pull/push servers auto bulk_pull_account_server = std::make_shared (server, std::make_unique (message)); @@ -565,7 +565,7 @@ void nano::transport::tcp_server::bootstrap_message_visitor::bulk_push (const na { return; } - node->bootstrap_workers.push_task ([server = server] () { + node->bootstrap_workers.post ([server = server] () { // TODO: Add completion callback to bulk pull server auto bulk_push_server = std::make_shared (server); bulk_push_server->throttled_receive (); @@ -582,7 +582,7 @@ void nano::transport::tcp_server::bootstrap_message_visitor::frontier_req (const return; } - node->bootstrap_workers.push_task ([server = server, message = message] () { + node->bootstrap_workers.post ([server = server, message = message] () { // TODO: There should be no need to re-copy message as unique pointer, refactor those bulk/frontier pull/push servers auto response = std::make_shared (server, std::make_unique (message)); response->send_next (); diff --git a/nano/node/transport/tcp_socket.cpp b/nano/node/transport/tcp_socket.cpp index 0c0cad1010..e85ee7a894 100644 --- a/nano/node/transport/tcp_socket.cpp +++ b/nano/node/transport/tcp_socket.cpp @@ -274,7 +274,7 @@ void nano::transport::tcp_socket::ongoing_checkup () return; } - node_l->workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (node_l->network_params.network.is_dev_network () ? 1 : 5), [this_w = weak_from_this ()] () { + node_l->workers.post_delayed (std::chrono::seconds (node_l->network_params.network.is_dev_network () ? 1 : 5), [this_w = weak_from_this ()] () { auto this_l = this_w.lock (); if (!this_l) { diff --git a/nano/node/wallet.cpp b/nano/node/wallet.cpp index 5364416907..18dd7df7a8 100644 --- a/nano/node/wallet.cpp +++ b/nano/node/wallet.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -19,6 +20,8 @@ #include +template class nano::store::typed_iterator; + nano::uint256_union nano::wallet_store::check (store::transaction const & transaction_a) { nano::wallet_value value (entry_get_raw (transaction_a, nano::wallet_store::check_special)); @@ -548,7 +551,8 @@ bool nano::wallet_store::exists (store::transaction const & transaction_a, nano: void nano::wallet_store::serialize_json (store::transaction const & transaction_a, std::string & string_a) { boost::property_tree::ptree tree; - for (store::iterator i (std::make_unique> (transaction_a, env, handle)), n (nullptr); i != n; ++i) + using iterator = store::typed_iterator; + for (iterator i{ store::iterator{ store::lmdb::iterator::begin (env.tx (transaction_a), handle) } }, n{ store::iterator{ store::lmdb::iterator::end (env.tx (transaction_a), handle) } }; i != n; ++i) { tree.put (i->first.to_string (), i->second.key.to_string ()); } @@ -1158,7 +1162,7 @@ void nano::wallet::work_ensure (nano::account const & account_a, nano::root cons wallets.delayed_work->operator[] (account_a) = root_a; - wallets.node.workers.add_timed_task (std::chrono::steady_clock::now () + precache_delay, [this_l = shared_from_this (), account_a, root_a] { + wallets.node.workers.post_delayed (precache_delay, [this_l = shared_from_this (), account_a, root_a] { auto delayed_work = this_l->wallets.delayed_work.lock (); auto existing (delayed_work->find (account_a)); if (existing != delayed_work->end () && existing->second == root_a) @@ -1359,13 +1363,15 @@ nano::wallets::wallets (bool error_a, nano::node & node_a) : status |= mdb_dbi_open (env.tx (transaction), "send_action_ids", MDB_CREATE, &send_action_ids); release_assert (status == 0); std::string beginning (nano::uint256_union (0).to_string ()); + nano::store::lmdb::db_val beginning_val{ beginning.size (), const_cast (beginning.c_str ()) }; std::string end ((nano::uint256_union (nano::uint256_t (0) - nano::uint256_t (1))).to_string ()); - store::iterator, nano::no_value> i (std::make_unique, nano::no_value>> (transaction, env, handle, nano::store::lmdb::db_val (beginning.size (), const_cast (beginning.c_str ())))); - store::iterator, nano::no_value> n (std::make_unique, nano::no_value>> (transaction, env, handle, nano::store::lmdb::db_val (end.size (), const_cast (end.c_str ())))); + nano::store::lmdb::db_val end_val{ end.size (), const_cast (end.c_str ()) }; + store::iterator i{ store::lmdb::iterator::lower_bound (env.tx (transaction), handle, beginning_val) }; + store::iterator n{ store::lmdb::iterator::lower_bound (env.tx (transaction), handle, end_val) }; for (; i != n; ++i) { nano::wallet_id id; - std::string text (i->first.data (), i->first.size ()); + std::string text (reinterpret_cast (i->first.data ()), i->first.size ()); auto error (id.decode_hex (text)); release_assert (!error); release_assert (items.find (id) == items.end ()); @@ -1488,13 +1494,15 @@ void nano::wallets::reload () auto transaction (tx_begin_write ()); std::unordered_set stored_items; std::string beginning (nano::uint256_union (0).to_string ()); + nano::store::lmdb::db_val beginning_val{ beginning.size (), const_cast (beginning.c_str ()) }; std::string end ((nano::uint256_union (nano::uint256_t (0) - nano::uint256_t (1))).to_string ()); - store::iterator, nano::no_value> i (std::make_unique, nano::no_value>> (transaction, env, handle, nano::store::lmdb::db_val (beginning.size (), const_cast (beginning.c_str ())))); - store::iterator, nano::no_value> n (std::make_unique, nano::no_value>> (transaction, env, handle, nano::store::lmdb::db_val (end.size (), const_cast (end.c_str ())))); + nano::store::lmdb::db_val end_val{ end.size (), const_cast (end.c_str ()) }; + store::iterator i{ store::lmdb::iterator::lower_bound (env.tx (transaction), handle, beginning_val) }; + store::iterator n{ store::lmdb::iterator::lower_bound (env.tx (transaction), handle, end_val) }; for (; i != n; ++i) { nano::wallet_id id; - std::string text (i->first.data (), i->first.size ()); + std::string text (reinterpret_cast (i->first.data ()), i->first.size ()); auto error (id.decode_hex (text)); debug_assert (!error); // New wallet @@ -1705,7 +1713,7 @@ void nano::wallets::ongoing_compute_reps () auto & node_l (node); // Representation drifts quickly on the test network but very slowly on the live network auto compute_delay = network_params.network.is_dev_network () ? std::chrono::milliseconds (10) : (network_params.network.is_test_network () ? std::chrono::milliseconds (nano::test_scan_wallet_reps_delay ()) : std::chrono::minutes (15)); - node.workers.add_timed_task (std::chrono::steady_clock::now () + compute_delay, [&node_l] () { + node.workers.post_delayed (compute_delay, [&node_l] () { node_l.wallets.ongoing_compute_reps (); }); } @@ -1755,20 +1763,22 @@ nano::uint128_t const nano::wallets::high_priority = std::numeric_limits iterator { - iterator result (std::make_unique> (transaction_a, env, handle, nano::store::lmdb::db_val (nano::account (special_count)))); - return result; + nano::account account{ special_count }; + nano::store::lmdb::db_val val{ account }; + return iterator{ store::iterator{ store::lmdb::iterator::lower_bound (env.tx (transaction_a), handle, val) } }; } auto nano::wallet_store::begin (store::transaction const & transaction_a, nano::account const & key) -> iterator { - iterator result (std::make_unique> (transaction_a, env, handle, nano::store::lmdb::db_val (key))); - return result; + nano::account account (key); + nano::store::lmdb::db_val val{ account }; + return iterator{ store::iterator{ store::lmdb::iterator::lower_bound (env.tx (transaction_a), handle, val) } }; } auto nano::wallet_store::find (store::transaction const & transaction_a, nano::account const & key) -> iterator { - auto result (begin (transaction_a, key)); - iterator end{ nullptr }; + auto result = begin (transaction_a, key); + auto end = this->end (transaction_a); if (result != end) { if (result->first == key) @@ -1789,7 +1799,7 @@ auto nano::wallet_store::find (store::transaction const & transaction_a, nano::a auto nano::wallet_store::end (store::transaction const & transaction_a) -> iterator { - return iterator{ nullptr }; + return iterator{ store::iterator{ store::lmdb::iterator::end (env.tx (transaction_a), handle) } }; } nano::mdb_wallets_store::mdb_wallets_store (std::filesystem::path const & path_a, nano::lmdb_config const & lmdb_config_a) : diff --git a/nano/node/wallet.hpp b/nano/node/wallet.hpp index f3daef8170..9cb7aa50e7 100644 --- a/nano/node/wallet.hpp +++ b/nano/node/wallet.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -58,7 +59,7 @@ enum class key_type class wallet_store final { public: - using iterator = store::iterator; + using iterator = store::typed_iterator; public: wallet_store (bool &, nano::kdf &, store::transaction &, store::lmdb::env &, nano::account, unsigned, std::string const &); diff --git a/nano/qt/qt.cpp b/nano/qt/qt.cpp index 7674093709..9005a0773b 100644 --- a/nano/qt/qt.cpp +++ b/nano/qt/qt.cpp @@ -107,7 +107,7 @@ nano_qt::self_pane::self_pane (nano_qt::wallet & wallet_a, nano::account const & QObject::connect (copy_button, &QPushButton::clicked, [this] () { this->wallet.application.clipboard ()->setText (QString (this->wallet.account.to_account ().c_str ())); copy_button->setText ("Copied!"); - this->wallet.node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (2), [this] () { + this->wallet.node.workers.post_delayed (std::chrono::seconds (2), [this] () { this->wallet.application.postEvent (&this->wallet.processor, new eventloop_event ([this] () { copy_button->setText ("Copy"); })); @@ -201,7 +201,7 @@ nano_qt::accounts::accounts (nano_qt::wallet & wallet_a) : this->wallet.wallet_m->deterministic_insert (transaction); show_button_success (*create_account); create_account->setText ("New account was created"); - this->wallet.node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (5), [this] () { + this->wallet.node.workers.post_delayed (std::chrono::seconds (5), [this] () { this->wallet.application.postEvent (&this->wallet.processor, new eventloop_event ([this] () { show_button_ok (*create_account); create_account->setText ("Create account"); @@ -212,7 +212,7 @@ nano_qt::accounts::accounts (nano_qt::wallet & wallet_a) : { show_button_error (*create_account); create_account->setText ("Wallet is locked, unlock it to create account"); - this->wallet.node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (5), [this] () { + this->wallet.node.workers.post_delayed (std::chrono::seconds (5), [this] () { this->wallet.application.postEvent (&this->wallet.processor, new eventloop_event ([this] () { show_button_ok (*create_account); create_account->setText ("Create account"); @@ -234,7 +234,7 @@ nano_qt::accounts::accounts (nano_qt::wallet & wallet_a) : this->wallet.application.clipboard ()->setText (QString (seed.to_string ().c_str ())); show_button_success (*backup_seed); backup_seed->setText ("Seed was copied to clipboard"); - this->wallet.node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (5), [this] () { + this->wallet.node.workers.post_delayed (std::chrono::seconds (5), [this] () { this->wallet.application.postEvent (&this->wallet.processor, new eventloop_event ([this] () { show_button_ok (*backup_seed); backup_seed->setText ("Copy wallet seed to clipboard"); @@ -246,7 +246,7 @@ nano_qt::accounts::accounts (nano_qt::wallet & wallet_a) : this->wallet.application.clipboard ()->setText (""); show_button_error (*backup_seed); backup_seed->setText ("Wallet is locked, unlock it to enable the backup"); - this->wallet.node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (5), [this] () { + this->wallet.node.workers.post_delayed (std::chrono::seconds (5), [this] () { this->wallet.application.postEvent (&this->wallet.processor, new eventloop_event ([this] () { show_button_ok (*backup_seed); backup_seed->setText ("Copy wallet seed to clipboard"); @@ -280,7 +280,7 @@ void nano_qt::accounts::refresh_wallet_balance () final_text += "\nReady to receive: " + wallet.format_balance (pending); } wallet_balance_label->setText (QString (final_text.c_str ())); - this->wallet.node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (60), [this] () { + this->wallet.node.workers.post_delayed (std::chrono::seconds (60), [this] () { this->wallet.application.postEvent (&this->wallet.processor, new eventloop_event ([this] () { refresh_wallet_balance (); })); @@ -410,7 +410,7 @@ nano_qt::import::import (nano_qt::wallet & wallet_a) : show_line_error (*seed); show_button_error (*import_seed); import_seed->setText ("Wallet is locked, unlock it to enable the import"); - this->wallet.node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (10), [this] () { + this->wallet.node.workers.post_delayed (std::chrono::seconds (10), [this] () { this->wallet.application.postEvent (&this->wallet.processor, new eventloop_event ([this] () { show_line_ok (*seed); show_button_ok (*import_seed); @@ -427,7 +427,7 @@ nano_qt::import::import (nano_qt::wallet & wallet_a) : show_button_success (*import_seed); import_seed->setText ("Successful import of seed"); this->wallet.refresh (); - this->wallet.node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (5), [this] () { + this->wallet.node.workers.post_delayed (std::chrono::seconds (5), [this] () { this->wallet.application.postEvent (&this->wallet.processor, new eventloop_event ([this] () { show_button_ok (*import_seed); import_seed->setText ("Import seed"); @@ -447,7 +447,7 @@ nano_qt::import::import (nano_qt::wallet & wallet_a) : { import_seed->setText ("Incorrect seed. Only HEX characters allowed"); } - this->wallet.node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (5), [this] () { + this->wallet.node.workers.post_delayed (std::chrono::seconds (5), [this] () { this->wallet.application.postEvent (&this->wallet.processor, new eventloop_event ([this] () { show_button_ok (*import_seed); import_seed->setText ("Import seed"); @@ -460,7 +460,7 @@ nano_qt::import::import (nano_qt::wallet & wallet_a) : show_line_error (*clear_line); show_button_error (*import_seed); import_seed->setText ("Type words 'clear keys'"); - this->wallet.node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (5), [this] () { + this->wallet.node.workers.post_delayed (std::chrono::seconds (5), [this] () { this->wallet.application.postEvent (&this->wallet.processor, new eventloop_event ([this] () { show_button_ok (*import_seed); import_seed->setText ("Import seed"); @@ -555,7 +555,7 @@ class short_text_visitor : public nano::block_visitor void open_block (nano::open_block const & block_a) { type = "Receive"; - if (block_a.hashables.source != ledger.constants.genesis->account ()) + if (block_a.hashables.source != ledger.constants.genesis->account ().as_union ()) { auto account_l = ledger.any.block_account (transaction, block_a.hashables.source); auto amount_l = ledger.any.block_amount (transaction, block_a.hash ()); @@ -775,7 +775,7 @@ void nano_qt::block_viewer::rebroadcast_action (nano::block_hash const & hash_a) if (successor) { done = false; - wallet.node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (1), [this, successor] () { + wallet.node.workers.post_delayed (std::chrono::seconds (1), [this, successor] () { this->wallet.application.postEvent (&this->wallet.processor, new eventloop_event ([this, successor] () { rebroadcast_action (successor.value ()); })); @@ -1177,7 +1177,7 @@ void nano_qt::wallet::ongoing_refresh () } })); - node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (5), [wallet_w] () { + node.workers.post_delayed (std::chrono::seconds (5), [wallet_w] () { if (auto wallet_l = wallet_w.lock ()) { wallet_l->ongoing_refresh (); @@ -1261,7 +1261,7 @@ void nano_qt::wallet::start () { show_button_error (*this_l->send_blocks_send); this_l->send_blocks_send->setText ("Wallet is locked, unlock it to send"); - this_l->node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (5), [this_w] () { + this_l->node.workers.post_delayed (std::chrono::seconds (5), [this_w] () { if (auto this_l = this_w.lock ()) { this_l->application.postEvent (&this_l->processor, new eventloop_event ([this_w] () { @@ -1280,7 +1280,7 @@ void nano_qt::wallet::start () show_line_error (*this_l->send_count); show_button_error (*this_l->send_blocks_send); this_l->send_blocks_send->setText ("Not enough balance"); - this_l->node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (5), [this_w] () { + this_l->node.workers.post_delayed (std::chrono::seconds (5), [this_w] () { if (auto this_l = this_w.lock ()) { this_l->application.postEvent (&this_l->processor, new eventloop_event ([this_w] () { @@ -1299,7 +1299,7 @@ void nano_qt::wallet::start () show_line_error (*this_l->send_account); show_button_error (*this_l->send_blocks_send); this_l->send_blocks_send->setText ("Bad destination account"); - this_l->node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (5), [this_w] () { + this_l->node.workers.post_delayed (std::chrono::seconds (5), [this_w] () { if (auto this_l = this_w.lock ()) { this_l->application.postEvent (&this_l->processor, new eventloop_event ([this_w] () { @@ -1318,7 +1318,7 @@ void nano_qt::wallet::start () show_line_error (*this_l->send_count); show_button_error (*this_l->send_blocks_send); this_l->send_blocks_send->setText ("Bad amount number"); - this_l->node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (5), [this_w] () { + this_l->node.workers.post_delayed (std::chrono::seconds (5), [this_w] () { if (auto this_l = this_w.lock ()) { this_l->application.postEvent (&this_l->processor, new eventloop_event ([this_w] () { @@ -1494,7 +1494,7 @@ void nano_qt::wallet::update_connected () void nano_qt::wallet::empty_password () { - this->node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (3), [this] () { + this->node.workers.post_delayed (std::chrono::seconds (3), [this] () { auto transaction (wallet_m->wallets.tx_begin_write ()); wallet_m->enter_password (transaction, std::string ("")); }); @@ -1598,7 +1598,7 @@ nano_qt::settings::settings (nano_qt::wallet & wallet_a) : change->setText ("Password was changed"); this->wallet.node.logger.warn (nano::log::type::qt, "Wallet password changed"); update_locked (false, false); - this->wallet.node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (5), [this] () { + this->wallet.node.workers.post_delayed (std::chrono::seconds (5), [this] () { this->wallet.application.postEvent (&this->wallet.processor, new eventloop_event ([this] () { show_button_ok (*change); change->setText ("Set/Change password"); @@ -1616,7 +1616,7 @@ nano_qt::settings::settings (nano_qt::wallet & wallet_a) : { show_button_error (*change); change->setText ("Wallet is locked, unlock it"); - this->wallet.node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (5), [this] () { + this->wallet.node.workers.post_delayed (std::chrono::seconds (5), [this] () { this->wallet.application.postEvent (&this->wallet.processor, new eventloop_event ([this] () { show_button_ok (*change); change->setText ("Set/Change password"); @@ -1642,7 +1642,7 @@ nano_qt::settings::settings (nano_qt::wallet & wallet_a) : change_rep->setText ("Representative was changed"); current_representative->setText (QString (representative_l.to_account ().c_str ())); new_representative->clear (); - this->wallet.node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (5), [this] () { + this->wallet.node.workers.post_delayed (std::chrono::seconds (5), [this] () { this->wallet.application.postEvent (&this->wallet.processor, new eventloop_event ([this] () { show_button_ok (*change_rep); change_rep->setText ("Change representative"); @@ -1653,7 +1653,7 @@ nano_qt::settings::settings (nano_qt::wallet & wallet_a) : { show_button_error (*change_rep); change_rep->setText ("Wallet is locked, unlock it"); - this->wallet.node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (5), [this] () { + this->wallet.node.workers.post_delayed (std::chrono::seconds (5), [this] () { this->wallet.application.postEvent (&this->wallet.processor, new eventloop_event ([this] () { show_button_ok (*change_rep); change_rep->setText ("Change representative"); @@ -1666,7 +1666,7 @@ nano_qt::settings::settings (nano_qt::wallet & wallet_a) : show_line_error (*new_representative); show_button_error (*change_rep); change_rep->setText ("Invalid account"); - this->wallet.node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (5), [this] () { + this->wallet.node.workers.post_delayed (std::chrono::seconds (5), [this] () { this->wallet.application.postEvent (&this->wallet.processor, new eventloop_event ([this] () { show_line_ok (*new_representative); show_button_ok (*change_rep); @@ -1706,7 +1706,7 @@ nano_qt::settings::settings (nano_qt::wallet & wallet_a) : show_line_error (*password); show_button_error (*lock_toggle); lock_toggle->setText ("Invalid password"); - this->wallet.node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (5), [this] () { + this->wallet.node.workers.post_delayed (std::chrono::seconds (5), [this] () { this->wallet.application.postEvent (&this->wallet.processor, new eventloop_event ([this] () { show_line_ok (*password); show_button_ok (*lock_toggle); diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index 309290dc1d..ce050df029 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -68,7 +68,7 @@ TEST (rpc, wrapped_task) // Exception should get caught throw std::runtime_error (""); })); - system.nodes[0]->workers.push_task (task); + system.nodes[0]->workers.post (task); ASSERT_TIMELY_EQ (5s, response, true); } @@ -2628,7 +2628,7 @@ TEST (rpc, wallet_frontiers) frontiers.push_back (nano::account (i->second.get (""))); } ASSERT_EQ (1, frontiers.size ()); - ASSERT_EQ (node->latest (nano::dev::genesis_key.pub), frontiers[0]); + ASSERT_EQ (node->latest (nano::dev::genesis_key.pub), frontiers[0].as_union ()); } TEST (rpc, work_validate) diff --git a/nano/secure/common.hpp b/nano/secure/common.hpp index fd28d30b13..e1639bd265 100644 --- a/nano/secure/common.hpp +++ b/nano/secure/common.hpp @@ -21,61 +21,6 @@ #include #include -namespace boost -{ -template <> -struct hash<::nano::uint256_union> -{ - size_t operator() (::nano::uint256_union const & value_a) const - { - return std::hash<::nano::uint256_union> () (value_a); - } -}; - -template <> -struct hash<::nano::block_hash> -{ - size_t operator() (::nano::block_hash const & value_a) const - { - return std::hash<::nano::block_hash> () (value_a); - } -}; - -template <> -struct hash<::nano::hash_or_account> -{ - size_t operator() (::nano::hash_or_account const & data_a) const - { - return std::hash<::nano::hash_or_account> () (data_a); - } -}; - -template <> -struct hash<::nano::public_key> -{ - size_t operator() (::nano::public_key const & value_a) const - { - return std::hash<::nano::public_key> () (value_a); - } -}; -template <> -struct hash<::nano::uint512_union> -{ - size_t operator() (::nano::uint512_union const & value_a) const - { - return std::hash<::nano::uint512_union> () (value_a); - } -}; -template <> -struct hash<::nano::qualified_root> -{ - size_t operator() (::nano::qualified_root const & value_a) const - { - return std::hash<::nano::qualified_root> () (value_a); - } -}; -} - namespace nano { /** diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index ec5aa666da..d2857974de 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -1093,7 +1093,7 @@ class dependent_block_visitor : public nano::block_visitor } void open_block (nano::open_block const & block_a) override { - if (block_a.source_field ().value () != ledger.constants.genesis->account ()) + if (block_a.source_field ().value () != ledger.constants.genesis->account ().as_union ()) { result[0] = block_a.source_field ().value (); } diff --git a/nano/secure/pending_info.hpp b/nano/secure/pending_info.hpp index 2d57df9ba2..c00e1cdb39 100644 --- a/nano/secure/pending_info.hpp +++ b/nano/secure/pending_info.hpp @@ -67,9 +67,9 @@ namespace std template <> struct hash<::nano::pending_key> { - size_t operator() (::nano::pending_key const & data_a) const + size_t operator() (::nano::pending_key const & value) const { - return hash<::nano::uint512_union>{}({ ::nano::uint256_union{ data_a.account.number () }, data_a.hash }); + return hash<::nano::uint512_union>{}({ ::nano::uint256_union{ value.account.number () }, value.hash }); } }; } diff --git a/nano/slow_test/node.cpp b/nano/slow_test/node.cpp index c49f4d84de..b9c3731f69 100644 --- a/nano/slow_test/node.cpp +++ b/nano/slow_test/node.cpp @@ -104,7 +104,7 @@ TEST (system, receive_while_synchronizing) node1->start (); system.nodes.push_back (node1); ASSERT_NE (nullptr, nano::test::establish_tcp (system, *node1, node->network.endpoint ())); - node1->workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::milliseconds (200), ([&system, &key] () { + node1->workers.post_delayed (std::chrono::milliseconds (200), ([&system, &key] () { auto hash (system.wallet (0)->send_sync (nano::dev::genesis_key.pub, key.pub, system.nodes[0]->config.receive_minimum.number ())); auto transaction = system.nodes[0]->ledger.tx_begin_read (); auto block = system.nodes[0]->ledger.any.block_get (transaction, hash); diff --git a/nano/store/CMakeLists.txt b/nano/store/CMakeLists.txt index e5e6a8c47c..0fb509489a 100644 --- a/nano/store/CMakeLists.txt +++ b/nano/store/CMakeLists.txt @@ -8,7 +8,6 @@ add_library( db_val.hpp db_val_impl.hpp iterator.hpp - iterator_impl.hpp final_vote.hpp lmdb/account.hpp lmdb/block.hpp @@ -33,6 +32,8 @@ add_library( pending.hpp pruned.hpp rep_weight.hpp + reverse_iterator.hpp + reverse_iterator_templ.hpp rocksdb/account.hpp rocksdb/block.hpp rocksdb/confirmation_height.hpp @@ -47,9 +48,12 @@ add_library( rocksdb/rocksdb.hpp rocksdb/iterator.hpp rocksdb/transaction_impl.hpp + rocksdb/utility.hpp rocksdb/version.hpp tables.hpp transaction.hpp + typed_iterator.hpp + typed_iterator_templ.hpp version.hpp versioning.hpp account.cpp @@ -58,13 +62,13 @@ add_library( confirmation_height.cpp db_val.cpp iterator.cpp - iterator_impl.cpp final_vote.cpp lmdb/account.cpp lmdb/block.cpp lmdb/confirmation_height.cpp lmdb/db_val.cpp lmdb/final_vote.cpp + lmdb/iterator.cpp lmdb/lmdb.cpp lmdb/lmdb_env.cpp lmdb/transaction.cpp @@ -79,11 +83,13 @@ add_library( peer.cpp pending.cpp pruned.cpp + rep_weight.cpp rocksdb/account.cpp rocksdb/block.cpp rocksdb/confirmation_height.cpp rocksdb/db_val.cpp rocksdb/final_vote.cpp + rocksdb/iterator.cpp rocksdb/online_weight.cpp rocksdb/peer.cpp rocksdb/pending.cpp @@ -91,8 +97,10 @@ add_library( rocksdb/rep_weight.cpp rocksdb/rocksdb.cpp rocksdb/transaction.cpp + rocksdb/utility.cpp rocksdb/version.cpp transaction.cpp + typed_iterator.cpp version.cpp versioning.cpp write_queue.hpp diff --git a/nano/store/account.cpp b/nano/store/account.cpp index a3bb7a1970..61549712a3 100644 --- a/nano/store/account.cpp +++ b/nano/store/account.cpp @@ -1,4 +1,9 @@ #include +#include +#include + +template class nano::store::typed_iterator; +template class nano::store::reverse_iterator>; std::optional nano::store::account::get (store::transaction const & transaction, nano::account const & account) { @@ -13,3 +18,15 @@ std::optional nano::store::account::get (store::transaction return std::nullopt; } } + +auto nano::store::account::rbegin (store::transaction const & tx) const -> reverse_iterator +{ + auto iter = end (tx); + --iter; + return reverse_iterator{ std::move (iter) }; +} + +auto nano::store::account::rend (transaction const & tx) const -> reverse_iterator +{ + return reverse_iterator{ end (tx) }; +} diff --git a/nano/store/account.hpp b/nano/store/account.hpp index cc9b93f688..dab7556b18 100644 --- a/nano/store/account.hpp +++ b/nano/store/account.hpp @@ -3,7 +3,8 @@ #include #include #include -#include +#include +#include #include @@ -20,19 +21,21 @@ namespace nano::store class account { public: - using iterator = store::iterator; + using iterator = typed_iterator; + using reverse_iterator = store::reverse_iterator; public: - virtual void put (store::write_transaction const &, nano::account const &, nano::account_info const &) = 0; - virtual bool get (store::transaction const &, nano::account const &, nano::account_info &) = 0; - std::optional get (store::transaction const &, nano::account const &); - virtual void del (store::write_transaction const &, nano::account const &) = 0; - virtual bool exists (store::transaction const &, nano::account const &) = 0; - virtual size_t count (store::transaction const &) = 0; - virtual iterator begin (store::transaction const &, nano::account const &) const = 0; - virtual iterator begin (store::transaction const &) const = 0; - virtual iterator rbegin (store::transaction const &) const = 0; - virtual iterator end (store::transaction const & transaction_a) const = 0; - virtual void for_each_par (std::function const &) const = 0; + virtual void put (write_transaction const & tx, nano::account const &, nano::account_info const &) = 0; + virtual bool get (transaction const & tx, nano::account const &, nano::account_info &) = 0; + std::optional get (transaction const & tx, nano::account const &); + virtual void del (write_transaction const & tx, nano::account const &) = 0; + virtual bool exists (transaction const & tx, nano::account const &) = 0; + virtual size_t count (transaction const & tx) = 0; + virtual iterator begin (transaction const & tx, nano::account const &) const = 0; + virtual iterator begin (transaction const & tx) const = 0; + reverse_iterator rbegin (transaction const & tx) const; + reverse_iterator rend (transaction const & tx) const; + virtual iterator end (transaction const & tx) const = 0; + virtual void for_each_par (std::function const &) const = 0; }; } // namespace nano::store diff --git a/nano/store/block.cpp b/nano/store/block.cpp index 40d128b96e..272a252907 100644 --- a/nano/store/block.cpp +++ b/nano/store/block.cpp @@ -1 +1,4 @@ #include +#include + +template class nano::store::typed_iterator; diff --git a/nano/store/block.hpp b/nano/store/block.hpp index cf0464a527..10cda6d29d 100644 --- a/nano/store/block.hpp +++ b/nano/store/block.hpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include @@ -22,21 +22,21 @@ namespace nano::store class block { public: - using iterator = store::iterator; + using iterator = typed_iterator; public: - virtual void put (store::write_transaction const &, nano::block_hash const &, nano::block const &) = 0; - virtual void raw_put (store::write_transaction const &, std::vector const &, nano::block_hash const &) = 0; - virtual std::optional successor (store::transaction const &, nano::block_hash const &) const = 0; - virtual void successor_clear (store::write_transaction const &, nano::block_hash const &) = 0; - virtual std::shared_ptr get (store::transaction const &, nano::block_hash const &) const = 0; - virtual std::shared_ptr random (store::transaction const &) = 0; - virtual void del (store::write_transaction const &, nano::block_hash const &) = 0; - virtual bool exists (store::transaction const &, nano::block_hash const &) = 0; - virtual uint64_t count (store::transaction const &) = 0; - virtual iterator begin (store::transaction const &, nano::block_hash const &) const = 0; - virtual iterator begin (store::transaction const &) const = 0; - virtual iterator end (store::transaction const &) const = 0; - virtual void for_each_par (std::function const & action_a) const = 0; + virtual void put (write_transaction const & tx, nano::block_hash const &, nano::block const &) = 0; + virtual void raw_put (write_transaction const & tx, std::vector const &, nano::block_hash const &) = 0; + virtual std::optional successor (transaction const & tx, nano::block_hash const &) const = 0; + virtual void successor_clear (write_transaction const & tx, nano::block_hash const &) = 0; + virtual std::shared_ptr get (transaction const & tx, nano::block_hash const &) const = 0; + virtual std::shared_ptr random (transaction const & tx) = 0; + virtual void del (write_transaction const & tx, nano::block_hash const &) = 0; + virtual bool exists (transaction const & tx, nano::block_hash const &) = 0; + virtual uint64_t count (transaction const & tx) = 0; + virtual iterator begin (transaction const & tx, nano::block_hash const &) const = 0; + virtual iterator begin (transaction const & tx) const = 0; + virtual iterator end (transaction const & tx) const = 0; + virtual void for_each_par (std::function const & action_a) const = 0; }; } // namespace nano::store diff --git a/nano/store/confirmation_height.cpp b/nano/store/confirmation_height.cpp index dae07d69c7..630e72abd6 100644 --- a/nano/store/confirmation_height.cpp +++ b/nano/store/confirmation_height.cpp @@ -1,4 +1,7 @@ #include +#include + +template class nano::store::typed_iterator; std::optional nano::store::confirmation_height::get (store::transaction const & transaction, nano::account const & account) { diff --git a/nano/store/confirmation_height.hpp b/nano/store/confirmation_height.hpp index de3cc89e44..4bebe575da 100644 --- a/nano/store/confirmation_height.hpp +++ b/nano/store/confirmation_height.hpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include @@ -18,7 +18,7 @@ namespace nano::store class confirmation_height { public: - using iterator = store::iterator; + using iterator = typed_iterator; public: virtual void put (store::write_transaction const & transaction_a, nano::account const & account_a, nano::confirmation_height_info const & confirmation_height_info_a) = 0; diff --git a/nano/store/final_vote.cpp b/nano/store/final_vote.cpp index 0b4565c1ab..99ccfdbf5f 100644 --- a/nano/store/final_vote.cpp +++ b/nano/store/final_vote.cpp @@ -1 +1,4 @@ #include +#include + +template class nano::store::typed_iterator; diff --git a/nano/store/final_vote.hpp b/nano/store/final_vote.hpp index e9f9e34173..f6f96f9796 100644 --- a/nano/store/final_vote.hpp +++ b/nano/store/final_vote.hpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include @@ -18,7 +18,7 @@ namespace nano::store class final_vote { public: - using iterator = store::iterator; + using iterator = typed_iterator; public: virtual bool put (store::write_transaction const & transaction_a, nano::qualified_root const & root_a, nano::block_hash const & hash_a) = 0; diff --git a/nano/store/iterator.cpp b/nano/store/iterator.cpp index 15bf18facf..31b80166c7 100644 --- a/nano/store/iterator.cpp +++ b/nano/store/iterator.cpp @@ -1 +1,81 @@ +#include #include + +namespace nano::store +{ +void iterator::update () +{ + std::visit ([&] (auto && arg) { + if (!arg.is_end ()) + { + this->current = arg.span (); + } + else + { + current = std::monostate{}; + } + }, + internals); +} + +iterator::iterator (std::variant && internals) noexcept : + internals{ std::move (internals) } +{ + update (); +} + +iterator::iterator (iterator && other) noexcept : + internals{ std::move (other.internals) } +{ + current = std::move (other.current); +} + +auto iterator::operator= (iterator && other) noexcept -> iterator & +{ + internals = std::move (other.internals); + current = std::move (other.current); + return *this; +} + +auto iterator::operator++ () -> iterator & +{ + std::visit ([] (auto && arg) { + ++arg; + }, + internals); + update (); + return *this; +} + +auto iterator::operator-- () -> iterator & +{ + std::visit ([] (auto && arg) { + --arg; + }, + internals); + update (); + return *this; +} + +auto iterator::operator->() const -> const_pointer +{ + release_assert (!is_end ()); + return std::get_if (¤t); +} + +auto iterator::operator* () const -> const_reference +{ + release_assert (!is_end ()); + return std::get (current); +} + +auto iterator::operator== (iterator const & other) const -> bool +{ + return internals == other.internals; +} + +bool iterator::is_end () const +{ + return std::holds_alternative (current); +} +} diff --git a/nano/store/iterator.hpp b/nano/store/iterator.hpp index 7fe957d721..edef1171a4 100644 --- a/nano/store/iterator.hpp +++ b/nano/store/iterator.hpp @@ -1,69 +1,57 @@ #pragma once -#include +#include +#include +#include +#include #include +#include +#include namespace nano::store { /** - * Iterates the key/value pairs of a transaction + * @class iterator + * @brief A generic database iterator for LMDB or RocksDB. + * + * This class represents an iterator for either LMDB or RocksDB (Persistent Key-Value Store) databases. + * It is a circular iterator, meaning that the end() sentinel value is always in the iteration cycle. + * + * Key characteristics: + * - Decrementing the end iterator points to the last key in the database. + * - Incrementing the end iterator points to the first key in the database. + * - Internally uses either an LMDB or RocksDB iterator, abstracted through a std::variant. */ -template class iterator final { public: - iterator (std::nullptr_t) - { - } - iterator (std::unique_ptr> impl_a) : - impl (std::move (impl_a)) - { - impl->fill (current); - } - iterator (iterator && other_a) : - current (std::move (other_a.current)), - impl (std::move (other_a.impl)) - { - } - iterator & operator++ () - { - ++*impl; - impl->fill (current); - return *this; - } - iterator & operator-- () - { - --*impl; - impl->fill (current); - return *this; - } - iterator & operator= (iterator && other_a) noexcept - { - impl = std::move (other_a.impl); - current = std::move (other_a.current); - return *this; - } - iterator & operator= (iterator const &) = delete; - std::pair * operator->() - { - return ¤t; - } - std::pair const & operator* () const - { - return current; - } - bool operator== (iterator const & other_a) const - { - return (impl == nullptr && other_a.impl == nullptr) || (impl != nullptr && *impl == other_a.impl.get ()) || (other_a.impl != nullptr && *other_a.impl == impl.get ()); - } - bool operator!= (iterator const & other_a) const - { - return !(*this == other_a); - } + using iterator_category = std::bidirectional_iterator_tag; + using value_type = std::pair, std::span>; + using pointer = value_type *; + using const_pointer = value_type const *; + using reference = value_type &; + using const_reference = value_type const &; private: - std::pair current; - std::unique_ptr> impl; + std::variant internals; + std::variant current; + void update (); + +public: + iterator (std::variant && internals) noexcept; + + iterator (iterator const &) = delete; + auto operator= (iterator const &) -> iterator & = delete; + + iterator (iterator && other) noexcept; + auto operator= (iterator && other) noexcept -> iterator &; + + auto operator++ () -> iterator &; + auto operator-- () -> iterator &; + auto operator->() const -> const_pointer; + auto operator* () const -> const_reference; + auto operator== (iterator const & other) const -> bool; + bool is_end () const; }; } // namespace nano::store diff --git a/nano/store/iterator_impl.cpp b/nano/store/iterator_impl.cpp deleted file mode 100644 index 4abbeb3f2a..0000000000 --- a/nano/store/iterator_impl.cpp +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/nano/store/iterator_impl.hpp b/nano/store/iterator_impl.hpp deleted file mode 100644 index 8daf89eadc..0000000000 --- a/nano/store/iterator_impl.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include -#include - -#include - -namespace nano::store -{ -template -class iterator_impl -{ -public: - explicit iterator_impl (transaction const & transaction_a) : - txn{ transaction_a }, - transaction_epoch{ transaction_a.epoch () } - { - } - virtual ~iterator_impl () - { - debug_assert (transaction_epoch == txn.epoch (), "invalid iterator-transaction lifetime detected"); - } - - virtual iterator_impl & operator++ () = 0; - virtual iterator_impl & operator-- () = 0; - virtual bool operator== (iterator_impl const & other_a) const = 0; - virtual bool is_end_sentinal () const = 0; - virtual void fill (std::pair &) const = 0; - iterator_impl & operator= (iterator_impl const &) = delete; - bool operator== (iterator_impl const * other_a) const - { - return (other_a != nullptr && *this == *other_a) || (other_a == nullptr && is_end_sentinal ()); - } - bool operator!= (iterator_impl const & other_a) const - { - return !(*this == other_a); - } - -protected: - transaction const & txn; - transaction::epoch_t const transaction_epoch; -}; -} diff --git a/nano/store/lmdb/account.cpp b/nano/store/lmdb/account.cpp index 766daaa81a..872c906c70 100644 --- a/nano/store/lmdb/account.cpp +++ b/nano/store/lmdb/account.cpp @@ -45,22 +45,18 @@ size_t nano::store::lmdb::account::count (store::transaction const & transaction auto nano::store::lmdb::account::begin (store::transaction const & transaction, nano::account const & account) const -> iterator { - return store.make_iterator (transaction, tables::accounts, account); + lmdb::db_val val{ account }; + return iterator{ store::iterator{ lmdb::iterator::lower_bound (store.env.tx (transaction), accounts_handle, val) } }; } auto nano::store::lmdb::account::begin (store::transaction const & transaction) const -> iterator { - return store.make_iterator (transaction, tables::accounts); + return iterator{ store::iterator{ lmdb::iterator::begin (store.env.tx (transaction), accounts_handle) } }; } -auto nano::store::lmdb::account::rbegin (store::transaction const & transaction_a) const -> iterator +auto nano::store::lmdb::account::end (store::transaction const & tx) const -> iterator { - return store.make_iterator (transaction_a, tables::accounts, false); -} - -auto nano::store::lmdb::account::end (store::transaction const & transaction_a) const -> iterator -{ - return iterator{ nullptr }; + return iterator{ store::iterator{ lmdb::iterator::end (store.env.tx (tx), accounts_handle) } }; } void nano::store::lmdb::account::for_each_par (std::function const & action_a) const diff --git a/nano/store/lmdb/account.hpp b/nano/store/lmdb/account.hpp index 283083d8d1..d8e8ef34e6 100644 --- a/nano/store/lmdb/account.hpp +++ b/nano/store/lmdb/account.hpp @@ -24,7 +24,6 @@ class account : public nano::store::account size_t count (store::transaction const & transaction_a) override; iterator begin (store::transaction const & transaction_a, nano::account const & account_a) const override; iterator begin (store::transaction const & transaction_a) const override; - iterator rbegin (store::transaction const & transaction_a) const override; iterator end (store::transaction const & transaction_a) const override; void for_each_par (std::function const & action_a) const override; diff --git a/nano/store/lmdb/block.cpp b/nano/store/lmdb/block.cpp index 72c4b05764..218245b310 100644 --- a/nano/store/lmdb/block.cpp +++ b/nano/store/lmdb/block.cpp @@ -137,17 +137,18 @@ uint64_t nano::store::lmdb::block::count (store::transaction const & transaction auto nano::store::lmdb::block::begin (store::transaction const & transaction) const -> iterator { - return store.make_iterator (transaction, tables::blocks); + return iterator{ store::iterator{ lmdb::iterator::begin (store.env.tx (transaction), blocks_handle) } }; } auto nano::store::lmdb::block::begin (store::transaction const & transaction, nano::block_hash const & hash) const -> iterator { - return store.make_iterator (transaction, tables::blocks, hash); + lmdb::db_val val{ hash }; + return iterator{ store::iterator{ lmdb::iterator::lower_bound (store.env.tx (transaction), blocks_handle, val) } }; } auto nano::store::lmdb::block::end (store::transaction const & transaction_a) const -> iterator { - return iterator{ nullptr }; + return iterator{ store::iterator{ lmdb::iterator::end (store.env.tx (transaction_a), blocks_handle) } }; } void nano::store::lmdb::block::for_each_par (std::function const & action_a) const diff --git a/nano/store/lmdb/confirmation_height.cpp b/nano/store/lmdb/confirmation_height.cpp index c85e47547b..a6302ab5a0 100644 --- a/nano/store/lmdb/confirmation_height.cpp +++ b/nano/store/lmdb/confirmation_height.cpp @@ -61,17 +61,18 @@ void nano::store::lmdb::confirmation_height::clear (store::write_transaction con auto nano::store::lmdb::confirmation_height::begin (store::transaction const & transaction, nano::account const & account) const -> iterator { - return store.make_iterator (transaction, tables::confirmation_height, account); + lmdb::db_val val{ account }; + return iterator{ store::iterator{ lmdb::iterator::lower_bound (store.env.tx (transaction), confirmation_height_handle, val) } }; } auto nano::store::lmdb::confirmation_height::begin (store::transaction const & transaction) const -> iterator { - return store.make_iterator (transaction, tables::confirmation_height); + return iterator{ store::iterator{ lmdb::iterator::begin (store.env.tx (transaction), confirmation_height_handle) } }; } auto nano::store::lmdb::confirmation_height::end (store::transaction const & transaction_a) const -> iterator { - return iterator{ nullptr }; + return iterator{ store::iterator{ lmdb::iterator::end (store.env.tx (transaction_a), confirmation_height_handle) } }; } void nano::store::lmdb::confirmation_height::for_each_par (std::function const & action_a) const diff --git a/nano/store/lmdb/final_vote.cpp b/nano/store/lmdb/final_vote.cpp index aed69f25bf..c7fdf7acad 100644 --- a/nano/store/lmdb/final_vote.cpp +++ b/nano/store/lmdb/final_vote.cpp @@ -66,17 +66,18 @@ void nano::store::lmdb::final_vote::clear (store::write_transaction const & tran auto nano::store::lmdb::final_vote::begin (store::transaction const & transaction, nano::qualified_root const & root) const -> iterator { - return store.make_iterator (transaction, tables::final_votes, root); + lmdb::db_val val{ root }; + return iterator{ store::iterator{ lmdb::iterator::lower_bound (store.env.tx (transaction), final_votes_handle, val) } }; } auto nano::store::lmdb::final_vote::begin (store::transaction const & transaction) const -> iterator { - return store.make_iterator (transaction, tables::final_votes); + return iterator{ store::iterator{ lmdb::iterator::begin (store.env.tx (transaction), final_votes_handle) } }; } auto nano::store::lmdb::final_vote::end (store::transaction const & transaction_a) const -> iterator { - return iterator{ nullptr }; + return iterator{ store::iterator{ lmdb::iterator::end (store.env.tx (transaction_a), final_votes_handle) } }; } void nano::store::lmdb::final_vote::for_each_par (std::function const & action_a) const diff --git a/nano/store/lmdb/iterator.cpp b/nano/store/lmdb/iterator.cpp new file mode 100644 index 0000000000..76cfc35e5b --- /dev/null +++ b/nano/store/lmdb/iterator.cpp @@ -0,0 +1,133 @@ +#include +#include + +namespace nano::store::lmdb +{ +auto iterator::span () const -> std::pair, std::span> +{ + auto & current = operator* (); + std::span key{ reinterpret_cast (current.first.mv_data), current.first.mv_size }; + std::span value{ reinterpret_cast (current.second.mv_data), current.second.mv_size }; + return std::make_pair (key, value); +} + +auto iterator::is_end () const -> bool +{ + return std::holds_alternative (current); +} + +void iterator::update (int status) +{ + if (status == MDB_SUCCESS) + { + value_type init; + auto status = mdb_cursor_get (cursor, &init.first, &init.second, MDB_GET_CURRENT); + release_assert (status == MDB_SUCCESS); + current = init; + } + else + { + current = std::monostate{}; + } +} + +iterator::iterator (MDB_txn * tx, MDB_dbi dbi) noexcept +{ + auto open_status = mdb_cursor_open (tx, dbi, &cursor); + release_assert (open_status == MDB_SUCCESS); + this->current = std::monostate{}; +} + +auto iterator::begin (MDB_txn * tx, MDB_dbi dbi) -> iterator +{ + iterator result{ tx, dbi }; + ++result; + return result; +} + +auto iterator::end (MDB_txn * tx, MDB_dbi dbi) -> iterator +{ + return iterator{ tx, dbi }; +} + +auto iterator::lower_bound (MDB_txn * tx, MDB_dbi dbi, MDB_val const & lower_bound) -> iterator +{ + iterator result{ tx, dbi }; + auto status = mdb_cursor_get (result.cursor, const_cast (&lower_bound), nullptr, MDB_SET_RANGE); + result.update (status); + return std::move (result); +} + +iterator::iterator (iterator && other) noexcept +{ + *this = std::move (other); +} + +iterator::~iterator () +{ + if (cursor) + { + mdb_cursor_close (cursor); + } +} + +auto iterator::operator= (iterator && other) noexcept -> iterator & +{ + cursor = other.cursor; + other.cursor = nullptr; + current = other.current; + other.current = std::monostate{}; + return *this; +} + +auto iterator::operator++ () -> iterator & +{ + auto operation = is_end () ? MDB_FIRST : MDB_NEXT; + auto status = mdb_cursor_get (cursor, nullptr, nullptr, operation); + release_assert (status == MDB_SUCCESS || status == MDB_NOTFOUND); + update (status); + return *this; +} + +auto iterator::operator-- () -> iterator & +{ + auto operation = is_end () ? MDB_LAST : MDB_PREV; + auto status = mdb_cursor_get (cursor, nullptr, nullptr, operation); + release_assert (status == MDB_SUCCESS || status == MDB_NOTFOUND); + update (status); + return *this; +} + +auto iterator::operator->() const -> const_pointer +{ + release_assert (!is_end ()); + return std::get_if (¤t); +} + +auto iterator::operator* () const -> const_reference +{ + release_assert (!is_end ()); + return std::get (current); +} + +auto iterator::operator== (iterator const & other) const -> bool +{ + if (is_end () != other.is_end ()) + { + return false; + } + if (is_end ()) + { + return true; + } + auto & lhs = std::get (current); + auto & rhs = std::get (other.current); + auto result = lhs.first.mv_data == rhs.first.mv_data; + if (!result) + { + return result; + } + debug_assert (std::make_pair (lhs.first.mv_data, lhs.first.mv_size) == std::make_pair (rhs.first.mv_data, rhs.first.mv_size) && std::make_pair (lhs.second.mv_data, lhs.second.mv_size) == std::make_pair (rhs.second.mv_data, rhs.second.mv_size)); + return result; +} +} // namespace nano::store::lmdb diff --git a/nano/store/lmdb/iterator.hpp b/nano/store/lmdb/iterator.hpp index 621d698082..ff921ca3aa 100644 --- a/nano/store/lmdb/iterator.hpp +++ b/nano/store/lmdb/iterator.hpp @@ -1,310 +1,58 @@ #pragma once -#include -#include -#include -#include -#include +#include +#include +#include +#include #include namespace nano::store::lmdb { -template -class iterator : public iterator_impl -{ -public: - iterator (transaction const & transaction_a, env const & env_a, MDB_dbi db_a, MDB_val const & val_a = MDB_val{}, bool const direction_asc = true) : - iterator_impl (transaction_a) - { - auto status (mdb_cursor_open (env_a.tx (transaction_a), db_a, &cursor)); - release_assert (status == 0); - auto operation (MDB_SET_RANGE); - if (val_a.mv_size != 0) - { - current.first = val_a; - } - else - { - operation = direction_asc ? MDB_FIRST : MDB_LAST; - } - auto status2 (mdb_cursor_get (cursor, ¤t.first.value, ¤t.second.value, operation)); - release_assert (status2 == 0 || status2 == MDB_NOTFOUND); - if (status2 != MDB_NOTFOUND) - { - auto status3 (mdb_cursor_get (cursor, ¤t.first.value, ¤t.second.value, MDB_GET_CURRENT)); - release_assert (status3 == 0 || status3 == MDB_NOTFOUND); - if (current.first.size () != sizeof (T)) - { - clear (); - } - } - else - { - clear (); - } - } - - iterator () = default; - - iterator (iterator && other_a) - { - cursor = other_a.cursor; - other_a.cursor = nullptr; - current = other_a.current; - } - - iterator (iterator const &) = delete; - - ~iterator () - { - if (cursor != nullptr) - { - mdb_cursor_close (cursor); - } - } - - iterator_impl & operator++ () override - { - debug_assert (cursor != nullptr); - auto status (mdb_cursor_get (cursor, ¤t.first.value, ¤t.second.value, MDB_NEXT)); - release_assert (status == 0 || status == MDB_NOTFOUND); - if (status == MDB_NOTFOUND) - { - clear (); - } - if (current.first.size () != sizeof (T)) - { - clear (); - } - return *this; - } - - iterator_impl & operator-- () override - { - debug_assert (cursor != nullptr); - auto status (mdb_cursor_get (cursor, ¤t.first.value, ¤t.second.value, MDB_PREV)); - release_assert (status == 0 || status == MDB_NOTFOUND); - if (status == MDB_NOTFOUND) - { - clear (); - } - if (current.first.size () != sizeof (T)) - { - clear (); - } - return *this; - } - - std::pair, store::db_val> * operator->() - { - return ¤t; - } - - bool operator== (iterator const & base_a) const - { - auto const other_a (boost::polymorphic_downcast const *> (&base_a)); - auto result (current.first.data () == other_a->current.first.data ()); - debug_assert (!result || (current.first.size () == other_a->current.first.size ())); - debug_assert (!result || (current.second.data () == other_a->current.second.data ())); - debug_assert (!result || (current.second.size () == other_a->current.second.size ())); - return result; - } - - bool operator== (iterator_impl const & base_a) const override - { - auto const other_a (boost::polymorphic_downcast const *> (&base_a)); - auto result (current.first.data () == other_a->current.first.data ()); - debug_assert (!result || (current.first.size () == other_a->current.first.size ())); - debug_assert (!result || (current.second.data () == other_a->current.second.data ())); - debug_assert (!result || (current.second.size () == other_a->current.second.size ())); - return result; - } - - bool is_end_sentinal () const override - { - return current.first.size () == 0; - } - void fill (std::pair & value_a) const override - { - if (current.first.size () != 0) - { - value_a.first = static_cast (current.first); - } - else - { - value_a.first = T (); - } - if (current.second.size () != 0) - { - value_a.second = static_cast (current.second); - } - else - { - value_a.second = U (); - } - } - void clear () - { - current.first = store::db_val (); - current.second = store::db_val (); - debug_assert (is_end_sentinal ()); - } - - iterator & operator= (iterator && other_a) - { - if (cursor != nullptr) - { - mdb_cursor_close (cursor); - } - cursor = other_a.cursor; - other_a.cursor = nullptr; - current = other_a.current; - other_a.clear (); - return *this; - } - - iterator_impl & operator= (iterator_impl const &) = delete; - MDB_cursor * cursor{ nullptr }; - std::pair, store::db_val> current; -}; - /** - * Iterates the key/value pairs of two stores merged together + * @class iterator + * @brief An LMDB database iterator. + * + * This class represents an iterator for LMDB (Lightning Memory-Mapped Database) databases. + * It is a circular iterator, meaning that the end() sentinel value is always in the iteration cycle. + * + * Key characteristics: + * - Decrementing the end iterator points to the last key in the database. + * - Incrementing the end iterator points to the first key in the database. */ -template -class merge_iterator : public iterator_impl +class iterator { -public: - merge_iterator (transaction const & transaction_a, MDB_dbi db1_a, MDB_dbi db2_a) : - impl1 (std::make_unique> (transaction_a, db1_a)), - impl2 (std::make_unique> (transaction_a, db2_a)) - { - } - - merge_iterator () : - impl1 (std::make_unique> ()), - impl2 (std::make_unique> ()) - { - } - - merge_iterator (transaction const & transaction_a, MDB_dbi db1_a, MDB_dbi db2_a, MDB_val const & val_a) : - impl1 (std::make_unique> (transaction_a, db1_a, val_a)), - impl2 (std::make_unique> (transaction_a, db2_a, val_a)) - { - } - - merge_iterator (merge_iterator && other_a) - { - impl1 = std::move (other_a.impl1); - impl2 = std::move (other_a.impl2); - } - - merge_iterator (merge_iterator const &) = delete; - - iterator_impl & operator++ () override - { - ++least_iterator (); - return *this; - } - - iterator_impl & operator-- () override - { - --least_iterator (); - return *this; - } - - std::pair, store::db_val> * operator->() - { - return least_iterator ().operator->(); - } - - bool operator== (merge_iterator const & other) const - { - return *impl1 == *other.impl1 && *impl2 == *other.impl2; - } - - bool operator!= (merge_iterator const & base_a) const - { - return !(*this == base_a); - } - - bool operator== (iterator_impl const & base_a) const override - { - debug_assert ((dynamic_cast const *> (&base_a) != nullptr) && "Incompatible iterator comparison"); - auto & other (static_cast const &> (base_a)); - return *this == other; - } - - bool is_end_sentinal () const override - { - return least_iterator ().is_end_sentinal (); - } - - void fill (std::pair & value_a) const override - { - auto & current (least_iterator ()); - if (current->first.size () != 0) - { - value_a.first = static_cast (current->first); - } - else - { - value_a.first = T (); - } - if (current->second.size () != 0) - { - value_a.second = static_cast (current->second); - } - else - { - value_a.second = U (); - } - } - merge_iterator & operator= (merge_iterator &&) = default; - merge_iterator & operator= (merge_iterator const &) = delete; - - mutable bool from_first_database{ false }; - -private: - iterator & least_iterator () const - { - iterator * result; - if (impl1->is_end_sentinal ()) - { - result = impl2.get (); - from_first_database = false; - } - else if (impl2->is_end_sentinal ()) - { - result = impl1.get (); - from_first_database = true; - } - else - { - auto key_cmp (mdb_cmp (mdb_cursor_txn (impl1->cursor), mdb_cursor_dbi (impl1->cursor), impl1->current.first, impl2->current.first)); - - if (key_cmp < 0) - { - result = impl1.get (); - from_first_database = true; - } - else if (key_cmp > 0) - { - result = impl2.get (); - from_first_database = false; - } - else - { - auto val_cmp (mdb_cmp (mdb_cursor_txn (impl1->cursor), mdb_cursor_dbi (impl1->cursor), impl1->current.second, impl2->current.second)); - result = val_cmp < 0 ? impl1.get () : impl2.get (); - from_first_database = (result == impl1.get ()); - } - } - return *result; - } + MDB_cursor * cursor{ nullptr }; + std::variant> current; + void update (int status); + iterator (MDB_txn * tx, MDB_dbi dbi) noexcept; - std::unique_ptr> impl1; - std::unique_ptr> impl2; +public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = std::pair; + using pointer = value_type *; + using const_pointer = value_type const *; + using reference = value_type &; + using const_reference = value_type const &; + + static auto begin (MDB_txn * tx, MDB_dbi dbi) -> iterator; + static auto end (MDB_txn * tx, MDB_dbi dbi) -> iterator; + static auto lower_bound (MDB_txn * tx, MDB_dbi dbi, MDB_val const & lower_bound) -> iterator; + + ~iterator (); + + iterator (iterator const &) = delete; + auto operator= (iterator const &) -> iterator & = delete; + + iterator (iterator && other_a) noexcept; + auto operator= (iterator && other) noexcept -> iterator &; + + auto operator++ () -> iterator &; + auto operator-- () -> iterator &; + auto operator->() const -> const_pointer; + auto operator* () const -> const_reference; + auto operator== (iterator const & other) const -> bool; + auto span () const -> std::pair, std::span>; + bool is_end () const; }; } diff --git a/nano/store/lmdb/lmdb.cpp b/nano/store/lmdb/lmdb.cpp index ef467208b2..a16d53cb88 100644 --- a/nano/store/lmdb/lmdb.cpp +++ b/nano/store/lmdb/lmdb.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -14,6 +15,8 @@ #include +template class nano::store::typed_iterator; + nano::store::lmdb::component::component (nano::logger & logger_a, std::filesystem::path const & path_a, nano::ledger_constants & constants, nano::txn_tracking_config const & txn_tracking_config_a, std::chrono::milliseconds block_processor_batch_max_time_a, nano::lmdb_config const & lmdb_config_a, bool backup_before_upgrade_a) : // clang-format off nano::store::component{ @@ -264,8 +267,8 @@ void nano::store::lmdb::component::upgrade_v22_to_v23 (store::write_transaction auto transaction = tx_begin_read (); // Manually create v22 compatible iterator to read accounts - auto it = make_iterator (transaction, tables::accounts); - auto const end = store::iterator (nullptr); + auto it = typed_iterator{ store::iterator{ iterator::begin (env.tx (transaction), account_store.accounts_handle) } }; + auto const end = typed_iterator{ store::iterator{ iterator::end (env.tx (transaction), account_store.accounts_handle) } }; for (; it != end; ++it) { @@ -457,7 +460,7 @@ void nano::store::lmdb::component::rebuild_db (store::write_transaction const & MDB_dbi temp; mdb_dbi_open (env.tx (transaction_a), "temp_table", MDB_CREATE, &temp); // Copy all values to temporary table - for (auto i (store::iterator (std::make_unique> (transaction_a, env, table))), n (store::iterator (nullptr)); i != n; ++i) + for (typed_iterator i{ store::iterator{ iterator::begin (env.tx (transaction_a), table) } }, n{ store::iterator{ iterator::end (env.tx (transaction_a), table) } }; i != n; ++i) { auto s = mdb_put (env.tx (transaction_a), temp, nano::store::lmdb::db_val (i->first), i->second, MDB_APPEND); release_assert_success (s); @@ -466,7 +469,7 @@ void nano::store::lmdb::component::rebuild_db (store::write_transaction const & // Clear existing table mdb_drop (env.tx (transaction_a), table, 0); // Put values from copy - for (auto i (store::iterator (std::make_unique> (transaction_a, env, temp))), n (store::iterator (nullptr)); i != n; ++i) + for (typed_iterator i{ store::iterator{ iterator::begin (env.tx (transaction_a), temp) } }, n{ store::iterator{ iterator::end (env.tx (transaction_a), temp) } }; i != n; ++i) { auto s = mdb_put (env.tx (transaction_a), table, nano::store::lmdb::db_val (i->first), i->second, MDB_APPEND); release_assert_success (s); @@ -480,7 +483,7 @@ void nano::store::lmdb::component::rebuild_db (store::write_transaction const & MDB_dbi temp; mdb_dbi_open (env.tx (transaction_a), "temp_table", MDB_CREATE, &temp); // Copy all values to temporary table - for (auto i (store::iterator (std::make_unique> (transaction_a, env, pending_store.pending_handle))), n (store::iterator (nullptr)); i != n; ++i) + for (typed_iterator i{ store::iterator{ iterator::begin (env.tx (transaction_a), pending_store.pending_handle) } }, n{ store::iterator{ iterator::end (env.tx (transaction_a), pending_store.pending_handle) } }; i != n; ++i) { auto s = mdb_put (env.tx (transaction_a), temp, nano::store::lmdb::db_val (i->first), nano::store::lmdb::db_val (i->second), MDB_APPEND); release_assert_success (s); @@ -488,7 +491,7 @@ void nano::store::lmdb::component::rebuild_db (store::write_transaction const & release_assert (count (transaction_a, pending_store.pending_handle) == count (transaction_a, temp)); mdb_drop (env.tx (transaction_a), pending_store.pending_handle, 0); // Put values from copy - for (auto i (store::iterator (std::make_unique> (transaction_a, env, temp))), n (store::iterator (nullptr)); i != n; ++i) + for (typed_iterator i{ store::iterator{ iterator::begin (env.tx (transaction_a), temp) } }, n{ store::iterator{ iterator::end (env.tx (transaction_a), temp) } }; i != n; ++i) { auto s = mdb_put (env.tx (transaction_a), pending_store.pending_handle, nano::store::lmdb::db_val (i->first), nano::store::lmdb::db_val (i->second), MDB_APPEND); release_assert_success (s); diff --git a/nano/store/lmdb/lmdb.hpp b/nano/store/lmdb/lmdb.hpp index 5920caa4b1..f1e786755f 100644 --- a/nano/store/lmdb/lmdb.hpp +++ b/nano/store/lmdb/lmdb.hpp @@ -93,18 +93,6 @@ class component : public nano::store::component bool copy_db (std::filesystem::path const & destination_file) override; void rebuild_db (store::write_transaction const & transaction_a) override; - template - store::iterator make_iterator (store::transaction const & transaction_a, tables table_a, bool const direction_asc = true) const - { - return store::iterator (std::make_unique> (transaction_a, env, table_to_dbi (table_a), nano::store::lmdb::db_val{}, direction_asc)); - } - - template - store::iterator make_iterator (store::transaction const & transaction_a, tables table_a, nano::store::lmdb::db_val const & key) const - { - return store::iterator (std::make_unique> (transaction_a, env, table_to_dbi (table_a), key)); - } - bool init_error () const override; uint64_t count (store::transaction const &, MDB_dbi) const; diff --git a/nano/store/lmdb/online_weight.cpp b/nano/store/lmdb/online_weight.cpp index a570b3ed8b..42c3cd5512 100644 --- a/nano/store/lmdb/online_weight.cpp +++ b/nano/store/lmdb/online_weight.cpp @@ -20,17 +20,12 @@ void nano::store::lmdb::online_weight::del (store::write_transaction const & tra auto nano::store::lmdb::online_weight::begin (store::transaction const & transaction) const -> iterator { - return store.make_iterator (transaction, tables::online_weight); -} - -auto nano::store::lmdb::online_weight::rbegin (store::transaction const & transaction) const -> iterator -{ - return store.make_iterator (transaction, tables::online_weight, false); + return iterator{ store::iterator{ lmdb::iterator::begin (store.env.tx (transaction), online_weight_handle) } }; } auto nano::store::lmdb::online_weight::end (store::transaction const & transaction_a) const -> iterator { - return iterator{ nullptr }; + return iterator{ store::iterator{ lmdb::iterator::end (store.env.tx (transaction_a), online_weight_handle) } }; } size_t nano::store::lmdb::online_weight::count (store::transaction const & transaction) const diff --git a/nano/store/lmdb/online_weight.hpp b/nano/store/lmdb/online_weight.hpp index 07668532c8..33db219667 100644 --- a/nano/store/lmdb/online_weight.hpp +++ b/nano/store/lmdb/online_weight.hpp @@ -16,7 +16,6 @@ class online_weight : public nano::store::online_weight void put (store::write_transaction const & transaction_a, uint64_t time_a, nano::amount const & amount_a) override; void del (store::write_transaction const & transaction_a, uint64_t time_a) override; iterator begin (store::transaction const & transaction_a) const override; - iterator rbegin (store::transaction const & transaction_a) const override; iterator end (store::transaction const & transaction_a) const override; size_t count (store::transaction const & transaction_a) const override; void clear (store::write_transaction const & transaction_a) override; diff --git a/nano/store/lmdb/peer.cpp b/nano/store/lmdb/peer.cpp index e22b9c27a0..4730c5a6b4 100644 --- a/nano/store/lmdb/peer.cpp +++ b/nano/store/lmdb/peer.cpp @@ -47,10 +47,10 @@ void nano::store::lmdb::peer::clear (store::write_transaction const & transactio auto nano::store::lmdb::peer::begin (store::transaction const & transaction) const -> iterator { - return store.make_iterator (transaction, tables::peers); + return iterator{ store::iterator{ lmdb::iterator::begin (store.env.tx (transaction), peers_handle) } }; } auto nano::store::lmdb::peer::end (store::transaction const & transaction_a) const -> iterator { - return iterator{ nullptr }; + return iterator{ store::iterator{ lmdb::iterator::end (store.env.tx (transaction_a), peers_handle) } }; } diff --git a/nano/store/lmdb/pending.cpp b/nano/store/lmdb/pending.cpp index fd2e573170..35e9a09324 100644 --- a/nano/store/lmdb/pending.cpp +++ b/nano/store/lmdb/pending.cpp @@ -47,17 +47,18 @@ bool nano::store::lmdb::pending::any (store::transaction const & transaction_a, auto nano::store::lmdb::pending::begin (store::transaction const & transaction_a, nano::pending_key const & key_a) const -> iterator { - return store.make_iterator (transaction_a, tables::pending, key_a); + lmdb::db_val val{ key_a }; + return iterator{ store::iterator{ lmdb::iterator::lower_bound (store.env.tx (transaction_a), pending_handle, val) } }; } auto nano::store::lmdb::pending::begin (store::transaction const & transaction_a) const -> iterator { - return store.make_iterator (transaction_a, tables::pending); + return iterator{ store::iterator{ lmdb::iterator::begin (store.env.tx (transaction_a), pending_handle) } }; } auto nano::store::lmdb::pending::end (store::transaction const & transaction_a) const -> iterator { - return iterator{ nullptr }; + return iterator{ store::iterator{ lmdb::iterator::end (store.env.tx (transaction_a), pending_handle) } }; } void nano::store::lmdb::pending::for_each_par (std::function const & action_a) const diff --git a/nano/store/lmdb/pruned.cpp b/nano/store/lmdb/pruned.cpp index 960881acc8..ca853bcab8 100644 --- a/nano/store/lmdb/pruned.cpp +++ b/nano/store/lmdb/pruned.cpp @@ -47,17 +47,18 @@ void nano::store::lmdb::pruned::clear (store::write_transaction const & transact auto nano::store::lmdb::pruned::begin (store::transaction const & transaction, nano::block_hash const & hash) const -> iterator { - return store.make_iterator (transaction, tables::pruned, hash); + lmdb::db_val val{ hash }; + return iterator{ store::iterator{ lmdb::iterator::lower_bound (store.env.tx (transaction), pruned_handle, val) } }; } auto nano::store::lmdb::pruned::begin (store::transaction const & transaction) const -> iterator { - return store.make_iterator (transaction, tables::pruned); + return iterator{ store::iterator{ lmdb::iterator::begin (store.env.tx (transaction), pruned_handle) } }; } auto nano::store::lmdb::pruned::end (store::transaction const & transaction_a) const -> iterator { - return iterator{ nullptr }; + return iterator{ store::iterator{ lmdb::iterator::end (store.env.tx (transaction_a), pruned_handle) } }; } void nano::store::lmdb::pruned::for_each_par (std::function const & action_a) const diff --git a/nano/store/lmdb/rep_weight.cpp b/nano/store/lmdb/rep_weight.cpp index 02e97a9d85..f44c1e51cc 100644 --- a/nano/store/lmdb/rep_weight.cpp +++ b/nano/store/lmdb/rep_weight.cpp @@ -45,17 +45,18 @@ void nano::store::lmdb::rep_weight::del (store::write_transaction const & txn_a, auto nano::store::lmdb::rep_weight::begin (store::transaction const & transaction_a, nano::account const & representative_a) const -> iterator { - return store.make_iterator (transaction_a, tables::rep_weights, representative_a); + lmdb::db_val val{ representative_a }; + return iterator{ store::iterator{ lmdb::iterator::lower_bound (store.env.tx (transaction_a), rep_weights_handle, val) } }; } auto nano::store::lmdb::rep_weight::begin (store::transaction const & transaction_a) const -> iterator { - return store.make_iterator (transaction_a, tables::rep_weights); + return iterator{ store::iterator{ lmdb::iterator::begin (store.env.tx (transaction_a), rep_weights_handle) } }; } auto nano::store::lmdb::rep_weight::end (store::transaction const & transaction_a) const -> iterator { - return iterator{ nullptr }; + return iterator{ store::iterator{ lmdb::iterator::end (store.env.tx (transaction_a), rep_weights_handle) } }; } void nano::store::lmdb::rep_weight::for_each_par (std::function const & action_a) const diff --git a/nano/store/online_weight.cpp b/nano/store/online_weight.cpp index 230a4f266d..4fec344f95 100644 --- a/nano/store/online_weight.cpp +++ b/nano/store/online_weight.cpp @@ -1 +1,18 @@ #include +#include +#include + +template class nano::store::typed_iterator; +template class nano::store::reverse_iterator>; + +auto nano::store::online_weight::rbegin (store::transaction const & tx) const -> reverse_iterator +{ + auto iter = end (tx); + --iter; + return reverse_iterator{ std::move (iter) }; +} + +auto nano::store::online_weight::rend (transaction const & tx) const -> reverse_iterator +{ + return reverse_iterator{ end (tx) }; +} diff --git a/nano/store/online_weight.hpp b/nano/store/online_weight.hpp index e6c90f8616..04e0780c80 100644 --- a/nano/store/online_weight.hpp +++ b/nano/store/online_weight.hpp @@ -2,7 +2,8 @@ #include #include -#include +#include +#include #include @@ -18,13 +19,15 @@ namespace nano::store class online_weight { public: - using iterator = store::iterator; + using iterator = typed_iterator; + using reverse_iterator = store::reverse_iterator; public: virtual void put (store::write_transaction const &, uint64_t, nano::amount const &) = 0; virtual void del (store::write_transaction const &, uint64_t) = 0; virtual iterator begin (store::transaction const &) const = 0; - virtual iterator rbegin (store::transaction const &) const = 0; + reverse_iterator rbegin (store::transaction const &) const; + reverse_iterator rend (store::transaction const &) const; virtual iterator end (store::transaction const & transaction_a) const = 0; virtual size_t count (store::transaction const &) const = 0; virtual void clear (store::write_transaction const &) = 0; diff --git a/nano/store/peer.cpp b/nano/store/peer.cpp index e442b57549..c682d3382e 100644 --- a/nano/store/peer.cpp +++ b/nano/store/peer.cpp @@ -1 +1,4 @@ #include +#include + +template class nano::store::typed_iterator; diff --git a/nano/store/peer.hpp b/nano/store/peer.hpp index 9a03721c9d..970e4c470f 100644 --- a/nano/store/peer.hpp +++ b/nano/store/peer.hpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include @@ -18,7 +18,7 @@ namespace nano::store class peer { public: - using iterator = store::iterator; + using iterator = typed_iterator; public: /// Returns true if the peer was inserted, false if it was already in the container diff --git a/nano/store/pending.cpp b/nano/store/pending.cpp index dd80f3579b..0b7731a025 100644 --- a/nano/store/pending.cpp +++ b/nano/store/pending.cpp @@ -1 +1,5 @@ +#include #include +#include + +template class nano::store::typed_iterator; diff --git a/nano/store/pending.hpp b/nano/store/pending.hpp index c9d2fd6593..fc7fabcb1c 100644 --- a/nano/store/pending.hpp +++ b/nano/store/pending.hpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include @@ -21,7 +21,7 @@ namespace nano::store class pending { public: - using iterator = store::iterator; + using iterator = typed_iterator; public: virtual void put (store::write_transaction const &, nano::pending_key const &, nano::pending_info const &) = 0; diff --git a/nano/store/pruned.cpp b/nano/store/pruned.cpp index e4342c7b4d..7b934566de 100644 --- a/nano/store/pruned.cpp +++ b/nano/store/pruned.cpp @@ -1 +1,4 @@ #include +#include + +template class nano::store::typed_iterator; diff --git a/nano/store/pruned.hpp b/nano/store/pruned.hpp index 448020c230..ff6345cf68 100644 --- a/nano/store/pruned.hpp +++ b/nano/store/pruned.hpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include @@ -18,7 +18,7 @@ namespace nano::store class pruned { public: - using iterator = store::iterator; + using iterator = typed_iterator; public: virtual void put (store::write_transaction const & transaction_a, nano::block_hash const & hash_a) = 0; diff --git a/nano/store/rep_weight.cpp b/nano/store/rep_weight.cpp new file mode 100644 index 0000000000..329e5c829f --- /dev/null +++ b/nano/store/rep_weight.cpp @@ -0,0 +1,4 @@ +#include +#include + +template class nano::store::typed_iterator; diff --git a/nano/store/rep_weight.hpp b/nano/store/rep_weight.hpp index b9fdc704ca..8d560ac519 100644 --- a/nano/store/rep_weight.hpp +++ b/nano/store/rep_weight.hpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include @@ -19,7 +19,7 @@ namespace nano::store class rep_weight { public: - using iterator = store::iterator; + using iterator = typed_iterator; public: virtual ~rep_weight (){}; diff --git a/nano/store/reverse_iterator.hpp b/nano/store/reverse_iterator.hpp new file mode 100644 index 0000000000..d15624b271 --- /dev/null +++ b/nano/store/reverse_iterator.hpp @@ -0,0 +1,48 @@ +#pragma once + +namespace nano::store +{ +/** + * @class reverse_iterator + * @brief A reverse iterator adaptor for bidirectional iterators. + * + * This class template adapts any bidirectional iterator to reverse its direction of iteration. + * It inverts the semantics of increment and decrement operations. + * + * Key characteristics: + * - Incrementing (operator++) moves to the previous element in the sequence. + * - Decrementing (operator--) moves to the next element in the sequence. + * - Dereferencing refers to the same element as the adapted iterator. + * - Compatible with any bidirectional iterator, not limited to specific container types. + */ +template +class reverse_iterator +{ +public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = Iter::value_type; + using pointer = value_type *; + using const_pointer = value_type const *; + using reference = value_type &; + using const_reference = value_type const &; + +private: + Iter internal; + +public: + reverse_iterator (Iter && other) noexcept; + + reverse_iterator (reverse_iterator const &) = delete; + auto operator= (reverse_iterator const &) -> reverse_iterator & = delete; + + reverse_iterator (reverse_iterator && other) noexcept; + auto operator= (reverse_iterator && other) noexcept -> reverse_iterator &; + + auto operator++ () -> reverse_iterator &; + auto operator-- () -> reverse_iterator &; + auto operator->() const -> const_pointer; + auto operator* () const -> const_reference; + auto operator== (reverse_iterator const & other) const -> bool; + bool is_end () const; +}; +} diff --git a/nano/store/reverse_iterator_templ.hpp b/nano/store/reverse_iterator_templ.hpp new file mode 100644 index 0000000000..c441045f73 --- /dev/null +++ b/nano/store/reverse_iterator_templ.hpp @@ -0,0 +1,63 @@ +#include + +namespace nano::store +{ +template +reverse_iterator::reverse_iterator (Iter && other) noexcept : + internal{ std::move (other) } +{ +} + +template +reverse_iterator::reverse_iterator (reverse_iterator && other) noexcept : + internal{ std::move (other.internal) } +{ +} + +template +auto reverse_iterator::operator= (reverse_iterator && other) noexcept -> reverse_iterator & +{ + internal = std::move (other.internal); + return *this; +} + +template +auto reverse_iterator::operator++ () -> reverse_iterator & +{ + --internal; + return *this; +} + +template +auto reverse_iterator::operator-- () -> reverse_iterator & +{ + ++internal; + return *this; +} + +template +auto reverse_iterator::operator->() const -> const_pointer +{ + release_assert (!is_end ()); + return internal.operator->(); +} + +template +auto reverse_iterator::operator* () const -> const_reference +{ + release_assert (!is_end ()); + return internal.operator* (); +} + +template +auto reverse_iterator::operator== (reverse_iterator const & other) const -> bool +{ + return internal == other.internal; +} + +template +bool reverse_iterator::is_end () const +{ + return internal.is_end (); +} +} diff --git a/nano/store/rocksdb/account.cpp b/nano/store/rocksdb/account.cpp index d0ffa20d92..5583c16574 100644 --- a/nano/store/rocksdb/account.cpp +++ b/nano/store/rocksdb/account.cpp @@ -1,6 +1,7 @@ #include #include #include +#include nano::store::rocksdb::account::account (nano::store::rocksdb::component & store_a) : store (store_a){}; @@ -44,22 +45,18 @@ size_t nano::store::rocksdb::account::count (store::transaction const & transact auto nano::store::rocksdb::account::begin (store::transaction const & transaction, nano::account const & account) const -> iterator { - return store.make_iterator (transaction, tables::accounts, account); + rocksdb::db_val val{ account }; + return iterator{ store::iterator{ rocksdb::iterator::lower_bound (store.db.get (), rocksdb::tx (transaction), store.table_to_column_family (tables::accounts), val) } }; } auto nano::store::rocksdb::account::begin (store::transaction const & transaction) const -> iterator { - return store.make_iterator (transaction, tables::accounts); + return iterator{ store::iterator{ rocksdb::iterator::begin (store.db.get (), rocksdb::tx (transaction), store.table_to_column_family (tables::accounts)) } }; } -auto nano::store::rocksdb::account::rbegin (store::transaction const & transaction_a) const -> iterator +auto nano::store::rocksdb::account::end (store::transaction const & transaction) const -> iterator { - return store.make_iterator (transaction_a, tables::accounts, false); -} - -auto nano::store::rocksdb::account::end (store::transaction const & transaction_a) const -> iterator -{ - return iterator{ nullptr }; + return iterator{ store::iterator{ rocksdb::iterator::end (store.db.get (), rocksdb::tx (transaction), store.table_to_column_family (tables::accounts)) } }; } void nano::store::rocksdb::account::for_each_par (std::function const & action_a) const diff --git a/nano/store/rocksdb/account.hpp b/nano/store/rocksdb/account.hpp index c0b73bde43..b67ef3ed2d 100644 --- a/nano/store/rocksdb/account.hpp +++ b/nano/store/rocksdb/account.hpp @@ -22,7 +22,6 @@ class account : public nano::store::account size_t count (store::transaction const & transaction_a) override; iterator begin (store::transaction const & transaction_a, nano::account const & account_a) const override; iterator begin (store::transaction const & transaction_a) const override; - iterator rbegin (store::transaction const & transaction_a) const override; iterator end (store::transaction const & transaction_a) const override; void for_each_par (std::function const & action_a) const override; }; diff --git a/nano/store/rocksdb/block.cpp b/nano/store/rocksdb/block.cpp index fcee0ffd36..6385704dae 100644 --- a/nano/store/rocksdb/block.cpp +++ b/nano/store/rocksdb/block.cpp @@ -2,6 +2,7 @@ #include #include #include +#include namespace nano { @@ -136,17 +137,18 @@ uint64_t nano::store::rocksdb::block::count (store::transaction const & transact auto nano::store::rocksdb::block::begin (store::transaction const & transaction) const -> iterator { - return store.make_iterator (transaction, tables::blocks); + return iterator{ store::iterator{ rocksdb::iterator::begin (store.db.get (), rocksdb::tx (transaction), store.table_to_column_family (tables::blocks)) } }; } auto nano::store::rocksdb::block::begin (store::transaction const & transaction, nano::block_hash const & hash) const -> iterator { - return store.make_iterator (transaction, tables::blocks, hash); + rocksdb::db_val val{ hash }; + return iterator{ store::iterator{ rocksdb::iterator::lower_bound (store.db.get (), rocksdb::tx (transaction), store.table_to_column_family (tables::blocks), val) } }; } auto nano::store::rocksdb::block::end (store::transaction const & transaction_a) const -> iterator { - return iterator{ nullptr }; + return iterator{ store::iterator{ rocksdb::iterator::end (store.db.get (), rocksdb::tx (transaction_a), store.table_to_column_family (tables::blocks)) } }; } void nano::store::rocksdb::block::for_each_par (std::function const & action_a) const diff --git a/nano/store/rocksdb/confirmation_height.cpp b/nano/store/rocksdb/confirmation_height.cpp index 838a365208..8f6578fa43 100644 --- a/nano/store/rocksdb/confirmation_height.cpp +++ b/nano/store/rocksdb/confirmation_height.cpp @@ -1,6 +1,7 @@ #include #include #include +#include nano::store::rocksdb::confirmation_height::confirmation_height (nano::store::rocksdb::component & store) : store{ store } @@ -61,17 +62,18 @@ void nano::store::rocksdb::confirmation_height::clear (store::write_transaction auto nano::store::rocksdb::confirmation_height::begin (store::transaction const & transaction, nano::account const & account) const -> iterator { - return store.make_iterator (transaction, tables::confirmation_height, account); + rocksdb::db_val val{ account }; + return iterator{ store::iterator{ rocksdb::iterator::lower_bound (store.db.get (), rocksdb::tx (transaction), store.table_to_column_family (tables::confirmation_height), val) } }; } auto nano::store::rocksdb::confirmation_height::begin (store::transaction const & transaction) const -> iterator { - return store.make_iterator (transaction, tables::confirmation_height); + return iterator{ store::iterator{ rocksdb::iterator::begin (store.db.get (), rocksdb::tx (transaction), store.table_to_column_family (tables::confirmation_height)) } }; } auto nano::store::rocksdb::confirmation_height::end (store::transaction const & transaction_a) const -> iterator { - return iterator{ nullptr }; + return iterator{ store::iterator{ rocksdb::iterator::end (store.db.get (), rocksdb::tx (transaction_a), store.table_to_column_family (tables::confirmation_height)) } }; } void nano::store::rocksdb::confirmation_height::for_each_par (std::function const & action_a) const diff --git a/nano/store/rocksdb/final_vote.cpp b/nano/store/rocksdb/final_vote.cpp index 328027ae3b..52afdfe34e 100644 --- a/nano/store/rocksdb/final_vote.cpp +++ b/nano/store/rocksdb/final_vote.cpp @@ -1,6 +1,7 @@ #include #include #include +#include nano::store::rocksdb::final_vote::final_vote (nano::store::rocksdb::component & store) : store{ store } {}; @@ -66,17 +67,18 @@ void nano::store::rocksdb::final_vote::clear (store::write_transaction const & t auto nano::store::rocksdb::final_vote::begin (store::transaction const & transaction, nano::qualified_root const & root) const -> iterator { - return store.make_iterator (transaction, tables::final_votes, root); + rocksdb::db_val val{ root }; + return iterator{ store::iterator{ rocksdb::iterator::lower_bound (store.db.get (), rocksdb::tx (transaction), store.table_to_column_family (tables::final_votes), val) } }; } auto nano::store::rocksdb::final_vote::begin (store::transaction const & transaction) const -> iterator { - return store.make_iterator (transaction, tables::final_votes); + return iterator{ store::iterator{ rocksdb::iterator::begin (store.db.get (), rocksdb::tx (transaction), store.table_to_column_family (tables::final_votes)) } }; } auto nano::store::rocksdb::final_vote::end (store::transaction const & transaction_a) const -> iterator { - return iterator{ nullptr }; + return iterator{ store::iterator{ rocksdb::iterator::end (store.db.get (), rocksdb::tx (transaction_a), store.table_to_column_family (tables::final_votes)) } }; } void nano::store::rocksdb::final_vote::for_each_par (std::function const & action_a) const diff --git a/nano/store/rocksdb/iterator.cpp b/nano/store/rocksdb/iterator.cpp new file mode 100644 index 0000000000..dde487c420 --- /dev/null +++ b/nano/store/rocksdb/iterator.cpp @@ -0,0 +1,146 @@ +#include +#include + +#include + +namespace nano::store::rocksdb +{ +auto iterator::span () const -> std::pair, std::span> +{ + auto & current = operator* (); + std::span key{ reinterpret_cast (current.first.data ()), current.first.size () }; + std::span value{ reinterpret_cast (current.second.data ()), current.second.size () }; + return std::make_pair (key, value); +} + +auto iterator::is_end () const -> bool +{ + return std::holds_alternative (current); +} + +void iterator::update () +{ + if (iter->Valid ()) + { + current = std::make_pair (iter->key (), iter->value ()); + } + else + { + current = std::monostate{}; + } +} + +iterator::iterator (decltype (iter) && iter) : + iter{ std::move (iter) } +{ + update (); +} + +auto iterator::begin (::rocksdb::DB * db, std::variant<::rocksdb::Transaction *, ::rocksdb::ReadOptions *> snapshot, ::rocksdb::ColumnFamilyHandle * table) -> iterator +{ + auto result = iterator{ make_iterator (db, snapshot, table) }; + ++result; + return result; +} + +auto iterator::end (::rocksdb::DB * db, std::variant<::rocksdb::Transaction *, ::rocksdb::ReadOptions *> snapshot, ::rocksdb::ColumnFamilyHandle * table) -> iterator +{ + return iterator{ make_iterator (db, snapshot, table) }; +} + +auto iterator::lower_bound (::rocksdb::DB * db, std::variant<::rocksdb::Transaction *, ::rocksdb::ReadOptions *> snapshot, ::rocksdb::ColumnFamilyHandle * table, ::rocksdb::Slice const & lower_bound) -> iterator +{ + auto iter = make_iterator (db, snapshot, table); + iter->Seek (lower_bound); + return iterator{ std::move (iter) }; +} + +auto iterator::make_iterator (::rocksdb::DB * db, std::variant<::rocksdb::Transaction *, ::rocksdb::ReadOptions *> snapshot, ::rocksdb::ColumnFamilyHandle * table) -> std::unique_ptr<::rocksdb::Iterator> +{ + return std::unique_ptr<::rocksdb::Iterator>{ std::visit ([&] (auto && ptr) { + using V = std::remove_cvref_t; + if constexpr (std::is_same_v) + { + ::rocksdb::ReadOptions ropts; + ropts.fill_cache = false; + return ptr->GetIterator (ropts, table); + } + else if constexpr (std::is_same_v) + { + ptr->fill_cache = false; + return db->NewIterator (*ptr, table); + } + else + { + static_assert (sizeof (V) == 0, "Missing variant handler for type V"); + } + }, + snapshot) }; +} + +iterator::iterator (iterator && other) noexcept +{ + *this = std::move (other); +} + +auto iterator::operator= (iterator && other) noexcept -> iterator & +{ + iter = std::move (other.iter); + current = other.current; + other.current = std::monostate{}; + return *this; +} + +auto iterator::operator++ () -> iterator & +{ + if (!is_end ()) + { + iter->Next (); + } + else + { + iter->SeekToFirst (); + } + update (); + return *this; +} + +auto iterator::operator-- () -> iterator & +{ + if (!is_end ()) + { + iter->Prev (); + } + else + { + iter->SeekToLast (); + } + update (); + return *this; +} + +auto iterator::operator->() const -> const_pointer +{ + release_assert (!is_end ()); + return std::get_if (¤t); +} + +auto iterator::operator* () const -> const_reference +{ + release_assert (!is_end ()); + return std::get (current); +} + +auto iterator::operator== (iterator const & other) const -> bool +{ + if (is_end () != other.is_end ()) + { + return false; + } + if (is_end ()) + { + return true; + } + return std::get (current) == std::get (other.current); +} +} // namespace nano::store::lmdb diff --git a/nano/store/rocksdb/iterator.hpp b/nano/store/rocksdb/iterator.hpp index 99fc9a9c54..5a460563f7 100644 --- a/nano/store/rocksdb/iterator.hpp +++ b/nano/store/rocksdb/iterator.hpp @@ -1,207 +1,60 @@ #pragma once -#include -#include -#include -#include +#include +#include +#include +#include +#include #include -#include -#include #include #include -namespace -{ -inline bool is_read (nano::store::transaction const & transaction_a) -{ - return (dynamic_cast (&transaction_a) != nullptr); -} - -inline rocksdb::ReadOptions & snapshot_options (nano::store::transaction const & transaction_a) -{ - debug_assert (is_read (transaction_a)); - return *static_cast (transaction_a.get_handle ()); -} -} - namespace nano::store::rocksdb { -template -class iterator : public iterator_impl +/** + * @class iterator + * @brief A RocksDB database iterator. + * + * This class represents an iterator for RocksDB (Persistent Key-Value Store) databases. + * It is a circular iterator, meaning that the end() sentinel value is always in the iteration cycle. + * + * Key characteristics: + * - Decrementing the end iterator points to the last key in the database. + * - Incrementing the end iterator points to the first key in the database. + */ +class iterator { -public: - iterator () = default; - - iterator (::rocksdb::DB * db, transaction const & transaction_a, ::rocksdb::ColumnFamilyHandle * handle_a, db_val const * val_a, bool const direction_asc) : - iterator_impl (transaction_a) - { - // Don't fill the block cache for any blocks read as a result of an iterator - if (is_read (transaction_a)) - { - auto read_options = snapshot_options (transaction_a); - read_options.fill_cache = false; - cursor.reset (db->NewIterator (read_options, handle_a)); - } - else - { - ::rocksdb::ReadOptions ropts; - ropts.fill_cache = false; - cursor.reset (tx (transaction_a)->GetIterator (ropts, handle_a)); - } - - if (val_a) - { - cursor->Seek (*val_a); - } - else if (direction_asc) - { - cursor->SeekToFirst (); - } - else - { - cursor->SeekToLast (); - } - - if (cursor->Valid ()) - { - current.first = cursor->key (); - current.second = cursor->value (); - } - else - { - clear (); - } - } - - iterator (::rocksdb::DB * db, store::transaction const & transaction_a, ::rocksdb::ColumnFamilyHandle * handle_a) : - iterator (db, transaction_a, handle_a, nullptr) - { - } - - iterator (iterator && other_a) - { - cursor = other_a.cursor; - other_a.cursor = nullptr; - current = other_a.current; - } - - iterator (iterator const &) = delete; + std::unique_ptr<::rocksdb::Iterator> iter; + std::variant> current; + void update (); + iterator (decltype (iter) && iter); + static auto make_iterator (::rocksdb::DB * db, std::variant<::rocksdb::Transaction *, ::rocksdb::ReadOptions *> snapshot, ::rocksdb::ColumnFamilyHandle * table) -> std::unique_ptr<::rocksdb::Iterator>; - iterator_impl & operator++ () override - { - cursor->Next (); - if (cursor->Valid ()) - { - current.first = cursor->key (); - current.second = cursor->value (); - - if (current.first.size () != sizeof (T)) - { - clear (); - } - } - else - { - clear (); - } - - return *this; - } - - iterator_impl & operator-- () override - { - cursor->Prev (); - if (cursor->Valid ()) - { - current.first = cursor->key (); - current.second = cursor->value (); - - if (current.first.size () != sizeof (T)) - { - clear (); - } - } - else - { - clear (); - } - - return *this; - } - - std::pair * operator->() - { - return ¤t; - } - - bool operator== (iterator_impl const & base_a) const override - { - auto const other_a (boost::polymorphic_downcast const *> (&base_a)); - - if (!current.first.data () && !other_a->current.first.data ()) - { - return true; - } - else if (!current.first.data () || !other_a->current.first.data ()) - { - return false; - } - - auto result (std::memcmp (current.first.data (), other_a->current.first.data (), current.first.size ()) == 0); - debug_assert (!result || (current.first.size () == other_a->current.first.size ())); - debug_assert (!result || std::memcmp (current.second.data (), other_a->current.second.data (), current.second.size ()) == 0); - debug_assert (!result || (current.second.size () == other_a->current.second.size ())); - return result; - } - - bool is_end_sentinal () const override - { - return current.first.size () == 0; - } - - void fill (std::pair & value_a) const override - { - { - if (current.first.size () != 0) - { - value_a.first = static_cast (current.first); - } - else - { - value_a.first = T (); - } - if (current.second.size () != 0) - { - value_a.second = static_cast (current.second); - } - else - { - value_a.second = U (); - } - } - } - void clear () - { - current.first = nano::store::rocksdb::db_val{}; - current.second = nano::store::rocksdb::db_val{}; - debug_assert (is_end_sentinal ()); - } - iterator & operator= (iterator && other_a) - { - cursor = std::move (other_a.cursor); - current = other_a.current; - return *this; - } - iterator_impl & operator= (iterator_impl const &) = delete; - - std::unique_ptr<::rocksdb::Iterator> cursor; - std::pair current; - -private: - ::rocksdb::Transaction * tx (store::transaction const & transaction_a) const - { - return static_cast<::rocksdb::Transaction *> (transaction_a.get_handle ()); - } +public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = std::pair<::rocksdb::Slice, ::rocksdb::Slice>; + using pointer = value_type *; + using const_pointer = value_type const *; + using reference = value_type &; + using const_reference = value_type const &; + + static auto begin (::rocksdb::DB * db, std::variant<::rocksdb::Transaction *, ::rocksdb::ReadOptions *> snapshot, ::rocksdb::ColumnFamilyHandle * table) -> iterator; + static auto end (::rocksdb::DB * db, std::variant<::rocksdb::Transaction *, ::rocksdb::ReadOptions *> snapshot, ::rocksdb::ColumnFamilyHandle * table) -> iterator; + static auto lower_bound (::rocksdb::DB * db, std::variant<::rocksdb::Transaction *, ::rocksdb::ReadOptions *> snapshot, ::rocksdb::ColumnFamilyHandle * table, ::rocksdb::Slice const & lower_bound) -> iterator; + + iterator (iterator const &) = delete; + auto operator= (iterator const &) -> iterator & = delete; + + iterator (iterator && other_a) noexcept; + auto operator= (iterator && other) noexcept -> iterator &; + + auto operator++ () -> iterator &; + auto operator-- () -> iterator &; + auto operator->() const -> const_pointer; + auto operator* () const -> const_reference; + auto operator== (iterator const & other) const -> bool; + auto span () const -> std::pair, std::span>; + bool is_end () const; }; } diff --git a/nano/store/rocksdb/online_weight.cpp b/nano/store/rocksdb/online_weight.cpp index dafd0f51f1..5d7a403101 100644 --- a/nano/store/rocksdb/online_weight.cpp +++ b/nano/store/rocksdb/online_weight.cpp @@ -1,5 +1,6 @@ #include #include +#include nano::store::rocksdb::online_weight::online_weight (nano::store::rocksdb::component & store_a) : store{ store_a } @@ -20,17 +21,12 @@ void nano::store::rocksdb::online_weight::del (store::write_transaction const & auto nano::store::rocksdb::online_weight::begin (store::transaction const & transaction) const -> iterator { - return store.make_iterator (transaction, tables::online_weight); -} - -auto nano::store::rocksdb::online_weight::rbegin (store::transaction const & transaction) const -> iterator -{ - return store.make_iterator (transaction, tables::online_weight, false); + return iterator{ store::iterator{ rocksdb::iterator::begin (store.db.get (), rocksdb::tx (transaction), store.table_to_column_family (tables::online_weight)) } }; } auto nano::store::rocksdb::online_weight::end (store::transaction const & transaction_a) const -> iterator { - return iterator{ nullptr }; + return iterator{ store::iterator{ rocksdb::iterator::end (store.db.get (), rocksdb::tx (transaction_a), store.table_to_column_family (tables::online_weight)) } }; } size_t nano::store::rocksdb::online_weight::count (store::transaction const & transaction) const diff --git a/nano/store/rocksdb/online_weight.hpp b/nano/store/rocksdb/online_weight.hpp index 1b88d329da..1c2d835a4a 100644 --- a/nano/store/rocksdb/online_weight.hpp +++ b/nano/store/rocksdb/online_weight.hpp @@ -18,7 +18,6 @@ class online_weight : public nano::store::online_weight void put (store::write_transaction const & transaction_a, uint64_t time_a, nano::amount const & amount_a) override; void del (store::write_transaction const & transaction_a, uint64_t time_a) override; iterator begin (store::transaction const & transaction_a) const override; - iterator rbegin (store::transaction const & transaction_a) const override; iterator end (store::transaction const & transaction_a) const override; size_t count (store::transaction const & transaction_a) const override; void clear (store::write_transaction const & transaction_a) override; diff --git a/nano/store/rocksdb/peer.cpp b/nano/store/rocksdb/peer.cpp index 0da5791028..0a7628a648 100644 --- a/nano/store/rocksdb/peer.cpp +++ b/nano/store/rocksdb/peer.cpp @@ -1,5 +1,6 @@ #include #include +#include nano::store::rocksdb::peer::peer (nano::store::rocksdb::component & store) : store{ store } {}; @@ -47,10 +48,10 @@ void nano::store::rocksdb::peer::clear (store::write_transaction const & transac auto nano::store::rocksdb::peer::begin (store::transaction const & transaction) const -> iterator { - return store.make_iterator (transaction, tables::peers); + return iterator{ store::iterator{ rocksdb::iterator::begin (store.db.get (), rocksdb::tx (transaction), store.table_to_column_family (tables::peers)) } }; } auto nano::store::rocksdb::peer::end (store::transaction const & transaction_a) const -> iterator { - return iterator{ nullptr }; + return iterator{ store::iterator{ rocksdb::iterator::end (store.db.get (), rocksdb::tx (transaction_a), store.table_to_column_family (tables::peers)) } }; } diff --git a/nano/store/rocksdb/pending.cpp b/nano/store/rocksdb/pending.cpp index 9fc4b1a2ef..1fc55e01ce 100644 --- a/nano/store/rocksdb/pending.cpp +++ b/nano/store/rocksdb/pending.cpp @@ -1,6 +1,7 @@ #include #include #include +#include nano::store::rocksdb::pending::pending (nano::store::rocksdb::component & store) : store{ store } {}; @@ -47,17 +48,18 @@ bool nano::store::rocksdb::pending::any (store::transaction const & transaction_ auto nano::store::rocksdb::pending::begin (store::transaction const & transaction_a, nano::pending_key const & key_a) const -> iterator { - return store.template make_iterator (transaction_a, tables::pending, key_a); + rocksdb::db_val val{ key_a }; + return iterator{ store::iterator{ rocksdb::iterator::lower_bound (store.db.get (), rocksdb::tx (transaction_a), store.table_to_column_family (tables::pending), val) } }; } auto nano::store::rocksdb::pending::begin (store::transaction const & transaction_a) const -> iterator { - return store.template make_iterator (transaction_a, tables::pending); + return iterator{ store::iterator{ rocksdb::iterator::begin (store.db.get (), rocksdb::tx (transaction_a), store.table_to_column_family (tables::pending)) } }; } auto nano::store::rocksdb::pending::end (store::transaction const & transaction_a) const -> iterator { - return iterator{ nullptr }; + return iterator{ store::iterator{ rocksdb::iterator::end (store.db.get (), rocksdb::tx (transaction_a), store.table_to_column_family (tables::pending)) } }; } void nano::store::rocksdb::pending::for_each_par (std::function const & action_a) const diff --git a/nano/store/rocksdb/pruned.cpp b/nano/store/rocksdb/pruned.cpp index 3cca201440..b85eae1ab7 100644 --- a/nano/store/rocksdb/pruned.cpp +++ b/nano/store/rocksdb/pruned.cpp @@ -1,6 +1,7 @@ #include #include #include +#include nano::store::rocksdb::pruned::pruned (nano::store::rocksdb::component & store_a) : store{ store_a } {}; @@ -47,17 +48,18 @@ void nano::store::rocksdb::pruned::clear (store::write_transaction const & trans auto nano::store::rocksdb::pruned::begin (store::transaction const & transaction_a, nano::block_hash const & hash_a) const -> iterator { - return store.make_iterator (transaction_a, tables::pruned, hash_a); + rocksdb::db_val val{ hash_a }; + return iterator{ store::iterator{ rocksdb::iterator::lower_bound (store.db.get (), rocksdb::tx (transaction_a), store.table_to_column_family (tables::pruned), val) } }; } auto nano::store::rocksdb::pruned::begin (store::transaction const & transaction_a) const -> iterator { - return store.make_iterator (transaction_a, tables::pruned); + return iterator{ store::iterator{ rocksdb::iterator::begin (store.db.get (), rocksdb::tx (transaction_a), store.table_to_column_family (tables::pruned)) } }; } auto nano::store::rocksdb::pruned::end (store::transaction const & transaction_a) const -> iterator { - return iterator{ nullptr }; + return iterator{ store::iterator{ rocksdb::iterator::end (store.db.get (), rocksdb::tx (transaction_a), store.table_to_column_family (tables::pruned)) } }; } void nano::store::rocksdb::pruned::for_each_par (std::function const & action_a) const diff --git a/nano/store/rocksdb/rep_weight.cpp b/nano/store/rocksdb/rep_weight.cpp index e719ef9904..3fc14c1722 100644 --- a/nano/store/rocksdb/rep_weight.cpp +++ b/nano/store/rocksdb/rep_weight.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include @@ -44,17 +45,18 @@ void nano::store::rocksdb::rep_weight::del (store::write_transaction const & txn auto nano::store::rocksdb::rep_weight::begin (store::transaction const & txn_a, nano::account const & representative_a) const -> iterator { - return store.make_iterator (txn_a, tables::rep_weights, representative_a); + rocksdb::db_val val{ representative_a }; + return iterator{ store::iterator{ rocksdb::iterator::lower_bound (store.db.get (), rocksdb::tx (txn_a), store.table_to_column_family (tables::rep_weights), val) } }; } auto nano::store::rocksdb::rep_weight::begin (store::transaction const & txn_a) const -> iterator { - return store.make_iterator (txn_a, tables::rep_weights); + return iterator{ store::iterator{ rocksdb::iterator::begin (store.db.get (), rocksdb::tx (txn_a), store.table_to_column_family (tables::rep_weights)) } }; } auto nano::store::rocksdb::rep_weight::end (store::transaction const & transaction_a) const -> iterator { - return iterator{ nullptr }; + return iterator{ store::iterator{ rocksdb::iterator::end (store.db.get (), rocksdb::tx (transaction_a), store.table_to_column_family (tables::rep_weights)) } }; } void nano::store::rocksdb::rep_weight::for_each_par (std::function const & action_a) const diff --git a/nano/store/rocksdb/rocksdb.cpp b/nano/store/rocksdb/rocksdb.cpp index e62d1db09e..fe6026fdfb 100644 --- a/nano/store/rocksdb/rocksdb.cpp +++ b/nano/store/rocksdb/rocksdb.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -301,8 +302,8 @@ void nano::store::rocksdb::component::upgrade_v22_to_v23 (store::write_transacti auto transaction = tx_begin_read (); // Manually create v22 compatible iterator to read accounts - auto it = make_iterator (transaction, tables::accounts); - auto const end = store::iterator (nullptr); + auto it = typed_iterator (store::iterator{ rocksdb::iterator::begin (db.get (), rocksdb::tx (transaction), table_to_column_family (tables::accounts)) }); + auto const end = typed_iterator{ store::iterator{ rocksdb::iterator::end (db.get (), rocksdb::tx (transaction), table_to_column_family (tables::accounts)) } }; for (; it != end; ++it) { @@ -499,19 +500,27 @@ rocksdb::ColumnFamilyHandle * nano::store::rocksdb::component::table_to_column_f bool nano::store::rocksdb::component::exists (store::transaction const & transaction_a, tables table_a, nano::store::rocksdb::db_val const & key_a) const { ::rocksdb::PinnableSlice slice; - ::rocksdb::Status status; - if (is_read (transaction_a)) - { - status = db->Get (snapshot_options (transaction_a), table_to_column_family (table_a), key_a, &slice); - } - else - { - ::rocksdb::ReadOptions options; - options.fill_cache = false; - status = tx (transaction_a)->Get (options, table_to_column_family (table_a), key_a, &slice); - } + auto internals = rocksdb::tx (transaction_a); + auto status = std::visit ([&] (auto && ptr) { + using V = std::remove_cvref_t; + if constexpr (std::is_same_v) + { + ::rocksdb::ReadOptions options; + options.fill_cache = false; + return ptr->Get (options, table_to_column_family (table_a), key_a, &slice); + } + else if constexpr (std::is_same_v) + { + return db->Get (*ptr, table_to_column_family (table_a), key_a, &slice); + } + else + { + static_assert (sizeof (V) == 0, "Missing variant handler for type V"); + } + }, + internals); - return (status.ok ()); + return status.ok (); } int nano::store::rocksdb::component::del (store::write_transaction const & transaction_a, tables table_a, nano::store::rocksdb::db_val const & key_a) @@ -520,7 +529,7 @@ int nano::store::rocksdb::component::del (store::write_transaction const & trans // RocksDB does not report not_found status, it is a pre-condition that the key exists debug_assert (exists (transaction_a, table_a, key_a)); flush_tombstones_check (table_a); - return tx (transaction_a)->Delete (table_to_column_family (table_a), key_a).code (); + return std::get<::rocksdb::Transaction *> (rocksdb::tx (transaction_a))->Delete (table_to_column_family (table_a), key_a).code (); } void nano::store::rocksdb::component::flush_tombstones_check (tables table_a) @@ -543,26 +552,28 @@ void nano::store::rocksdb::component::flush_table (nano::tables table_a) db->Flush (::rocksdb::FlushOptions{}, table_to_column_family (table_a)); } -rocksdb::Transaction * nano::store::rocksdb::component::tx (store::transaction const & transaction_a) const -{ - debug_assert (!is_read (transaction_a)); - return static_cast<::rocksdb::Transaction *> (transaction_a.get_handle ()); -} - int nano::store::rocksdb::component::get (store::transaction const & transaction_a, tables table_a, nano::store::rocksdb::db_val const & key_a, nano::store::rocksdb::db_val & value_a) const { ::rocksdb::ReadOptions options; ::rocksdb::PinnableSlice slice; auto handle = table_to_column_family (table_a); - ::rocksdb::Status status; - if (is_read (transaction_a)) - { - status = db->Get (snapshot_options (transaction_a), handle, key_a, &slice); - } - else - { - status = tx (transaction_a)->Get (options, handle, key_a, &slice); - } + auto internals = rocksdb::tx (transaction_a); + auto status = std::visit ([&] (auto && ptr) { + using V = std::remove_cvref_t; + if constexpr (std::is_same_v) + { + return ptr->Get (options, handle, key_a, &slice); + } + else if constexpr (std::is_same_v) + { + return db->Get (*ptr, handle, key_a, &slice); + } + else + { + static_assert (sizeof (V) == 0, "Missing variant handler for type V"); + } + }, + internals); if (status.ok ()) { @@ -576,8 +587,7 @@ int nano::store::rocksdb::component::get (store::transaction const & transaction int nano::store::rocksdb::component::put (store::write_transaction const & transaction_a, tables table_a, nano::store::rocksdb::db_val const & key_a, nano::store::rocksdb::db_val const & value_a) { debug_assert (transaction_a.contains (table_a)); - auto txn = tx (transaction_a); - return txn->Put (table_to_column_family (table_a), key_a, value_a).code (); + return std::get<::rocksdb::Transaction *> (rocksdb::tx (transaction_a))->Put (table_to_column_family (table_a), key_a, value_a).code (); } bool nano::store::rocksdb::component::not_found (int status) const diff --git a/nano/store/rocksdb/rocksdb.hpp b/nano/store/rocksdb/rocksdb.hpp index 1d964b8ee1..bd5cb6a79e 100644 --- a/nano/store/rocksdb/rocksdb.hpp +++ b/nano/store/rocksdb/rocksdb.hpp @@ -85,18 +85,6 @@ class component : public nano::store::component unsigned max_block_write_batch_num () const override; - template - store::iterator make_iterator (store::transaction const & transaction_a, tables table_a, bool const direction_asc = true) const - { - return store::iterator (std::make_unique> (db.get (), transaction_a, table_to_column_family (table_a), nullptr, direction_asc)); - } - - template - store::iterator make_iterator (store::transaction const & transaction_a, tables table_a, nano::store::rocksdb::db_val const & key) const - { - return store::iterator (std::make_unique> (db.get (), transaction_a, table_to_column_family (table_a), &key, true)); - } - bool init_error () const override; std::string error_string (int status) const override; @@ -122,7 +110,6 @@ class component : public nano::store::component std::unordered_map tombstone_map; std::unordered_map cf_name_table_map; - ::rocksdb::Transaction * tx (store::transaction const & transaction_a) const; std::vector all_tables () const; bool not_found (int status) const override; diff --git a/nano/store/rocksdb/utility.cpp b/nano/store/rocksdb/utility.cpp new file mode 100644 index 0000000000..092e75a9f8 --- /dev/null +++ b/nano/store/rocksdb/utility.cpp @@ -0,0 +1,11 @@ +#include +#include + +auto nano::store::rocksdb::tx (store::transaction const & transaction_a) -> std::variant<::rocksdb::Transaction *, ::rocksdb::ReadOptions *> +{ + if (dynamic_cast (&transaction_a) != nullptr) + { + return static_cast<::rocksdb::ReadOptions *> (transaction_a.get_handle ()); + } + return static_cast<::rocksdb::Transaction *> (transaction_a.get_handle ()); +} diff --git a/nano/store/rocksdb/utility.hpp b/nano/store/rocksdb/utility.hpp new file mode 100644 index 0000000000..7686b6b4d9 --- /dev/null +++ b/nano/store/rocksdb/utility.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include + +namespace nano::store +{ +class transaction; +} + +namespace nano::store::rocksdb +{ +auto tx (store::transaction const & transaction_a) -> std::variant<::rocksdb::Transaction *, ::rocksdb::ReadOptions *>; +} diff --git a/nano/store/typed_iterator.cpp b/nano/store/typed_iterator.cpp new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/nano/store/typed_iterator.cpp @@ -0,0 +1 @@ + diff --git a/nano/store/typed_iterator.hpp b/nano/store/typed_iterator.hpp new file mode 100644 index 0000000000..47f3f2e9dd --- /dev/null +++ b/nano/store/typed_iterator.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include + +namespace nano::store +{ +/** + * @class typed_iterator + * @brief A generic typed iterator for key-value stores. + * + * This class represents a typed iterator for key-value store databases, such as RocksDB. + * It supports typing for both keys and values, providing type-safe access to the database contents. + * + * Key characteristics: + * - Generic: Works with various key-value store implementations. + * - Type-safe: Supports strongly typed keys and values. + * - Circular: The end() sentinel value is always in the iteration cycle. + * - Automatic deserialization: When pointing to a valid non-sentinel location, it loads and + * deserializes the database value into the appropriate type. + * + * Behavior: + * - Decrementing the end iterator points to the last key-value pair in the database. + * - Incrementing the end iterator points to the first key-value pair in the database. + */ +template +class typed_iterator final +{ +public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = std::pair; + using pointer = value_type *; + using const_pointer = value_type const *; + using reference = value_type &; + using const_reference = value_type const &; + +private: + iterator iter; + std::variant current; + void update (); + +public: + typed_iterator (iterator && iter) noexcept; + + typed_iterator (typed_iterator const &) = delete; + auto operator= (typed_iterator const &) -> typed_iterator & = delete; + + typed_iterator (typed_iterator && other) noexcept; + auto operator= (typed_iterator && other) noexcept -> typed_iterator &; + + auto operator++ () -> typed_iterator &; + auto operator-- () -> typed_iterator &; + auto operator->() const -> const_pointer; + auto operator* () const -> const_reference; + auto operator== (typed_iterator const & other) const -> bool; + auto is_end () const -> bool; +}; +} // namespace nano::store diff --git a/nano/store/typed_iterator_templ.hpp b/nano/store/typed_iterator_templ.hpp new file mode 100644 index 0000000000..9ce464c16c --- /dev/null +++ b/nano/store/typed_iterator_templ.hpp @@ -0,0 +1,88 @@ +#include +#include +#include +#include + +namespace nano::store +{ +template +void typed_iterator::update () +{ + if (!iter.is_end ()) + { + // FIXME Don't convert via lmdb::db_val, this is just a placeholder + auto const & data = *iter; + lmdb::db_val key_val{ MDB_val{ data.first.size (), const_cast (reinterpret_cast (data.first.data ())) } }; + lmdb::db_val value_val{ MDB_val{ data.second.size (), const_cast (reinterpret_cast (data.second.data ())) } }; + current = std::make_pair (static_cast (key_val), static_cast (value_val)); + } + else + { + current = std::monostate{}; + } +} + +template +typed_iterator::typed_iterator (iterator && iter) noexcept : + iter{ std::move (iter) } +{ + update (); +} + +template +typed_iterator::typed_iterator (typed_iterator && other) noexcept : + iter{ std::move (other.iter) }, + current{ std::move (other.current) } +{ +} + +template +auto typed_iterator::operator= (typed_iterator && other) noexcept -> typed_iterator & +{ + iter = std::move (other.iter); + current = std::move (other.current); + return *this; +} + +template +auto typed_iterator::operator++ () -> typed_iterator & +{ + ++iter; + update (); + return *this; +} + +template +auto typed_iterator::operator-- () -> typed_iterator & +{ + --iter; + update (); + return *this; +} + +template +auto typed_iterator::operator->() const -> const_pointer +{ + release_assert (!is_end ()); + return std::get_if (¤t); +} + +template +auto typed_iterator::operator* () const -> const_reference +{ + release_assert (!is_end ()); + return std::get (current); +} + +template +auto typed_iterator::operator== (typed_iterator const & other) const -> bool +{ + return iter == other.iter; +} + +template +auto typed_iterator::is_end () const -> bool +{ + return std::holds_alternative (current); +} +} diff --git a/nano/test_common/system.cpp b/nano/test_common/system.cpp index 15aed96141..758a5c72ee 100644 --- a/nano/test_common/system.cpp +++ b/nano/test_common/system.cpp @@ -406,7 +406,7 @@ class traffic_generator : public std::enable_shared_from_this if (count_l > 0) { auto this_l (shared_from_this ()); - node->workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::milliseconds (wait), [this_l] () { this_l->run (); }); + node->workers.post_delayed (std::chrono::milliseconds (wait), [this_l] () { this_l->run (); }); } } std::vector accounts;