From 37f4f5c2d6fb385d62a72603ad21385b2cb3db50 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Mon, 14 Oct 2024 18:39:43 +0100 Subject: [PATCH] Rewrite nano::store iterators to use variants instead of polymorphism. --- nano/core_test/block_store.cpp | 10 +- nano/node/node.cpp | 2 +- nano/node/wallet.cpp | 38 ++- nano/node/wallet.hpp | 3 +- nano/store/CMakeLists.txt | 10 +- nano/store/account.cpp | 17 + nano/store/account.hpp | 29 +- nano/store/block.cpp | 3 + nano/store/block.hpp | 30 +- nano/store/confirmation_height.cpp | 3 + nano/store/confirmation_height.hpp | 4 +- nano/store/final_vote.cpp | 3 + nano/store/final_vote.hpp | 4 +- nano/store/iterator.cpp | 80 +++++ nano/store/iterator.hpp | 94 +++--- nano/store/iterator_impl.cpp | 1 - nano/store/iterator_impl.hpp | 43 --- nano/store/lmdb/account.cpp | 14 +- nano/store/lmdb/account.hpp | 1 - nano/store/lmdb/block.cpp | 7 +- nano/store/lmdb/confirmation_height.cpp | 7 +- nano/store/lmdb/final_vote.cpp | 7 +- nano/store/lmdb/iterator.cpp | 133 ++++++++ nano/store/lmdb/iterator.hpp | 342 +++------------------ nano/store/lmdb/lmdb.cpp | 15 +- nano/store/lmdb/lmdb.hpp | 12 - nano/store/lmdb/online_weight.cpp | 9 +- nano/store/lmdb/online_weight.hpp | 1 - nano/store/lmdb/peer.cpp | 4 +- nano/store/lmdb/pending.cpp | 7 +- nano/store/lmdb/pruned.cpp | 7 +- nano/store/lmdb/rep_weight.cpp | 7 +- nano/store/online_weight.cpp | 17 + nano/store/online_weight.hpp | 9 +- nano/store/peer.cpp | 3 + nano/store/peer.hpp | 4 +- nano/store/pending.cpp | 4 + nano/store/pending.hpp | 4 +- nano/store/pruned.cpp | 3 + nano/store/pruned.hpp | 4 +- nano/store/rep_weight.cpp | 4 + nano/store/rep_weight.hpp | 4 +- nano/store/reverse_iterator.hpp | 48 +++ nano/store/reverse_iterator_templ.hpp | 63 ++++ nano/store/rocksdb/account.cpp | 15 +- nano/store/rocksdb/account.hpp | 1 - nano/store/rocksdb/block.cpp | 8 +- nano/store/rocksdb/confirmation_height.cpp | 8 +- nano/store/rocksdb/final_vote.cpp | 8 +- nano/store/rocksdb/iterator.cpp | 146 +++++++++ nano/store/rocksdb/iterator.hpp | 230 +++----------- nano/store/rocksdb/online_weight.cpp | 10 +- nano/store/rocksdb/online_weight.hpp | 1 - nano/store/rocksdb/peer.cpp | 5 +- nano/store/rocksdb/pending.cpp | 8 +- nano/store/rocksdb/pruned.cpp | 8 +- nano/store/rocksdb/rep_weight.cpp | 8 +- nano/store/rocksdb/rocksdb.cpp | 4 +- nano/store/rocksdb/rocksdb.hpp | 12 - nano/store/typed_iterator.cpp | 1 + nano/store/typed_iterator.hpp | 63 ++++ nano/store/typed_iterator_templ.hpp | 88 ++++++ 62 files changed, 987 insertions(+), 751 deletions(-) delete mode 100644 nano/store/iterator_impl.cpp delete mode 100644 nano/store/iterator_impl.hpp create mode 100644 nano/store/lmdb/iterator.cpp create mode 100644 nano/store/rep_weight.cpp create mode 100644 nano/store/reverse_iterator.hpp create mode 100644 nano/store/reverse_iterator_templ.hpp create mode 100644 nano/store/rocksdb/iterator.cpp create mode 100644 nano/store/typed_iterator.cpp create mode 100644 nano/store/typed_iterator.hpp create mode 100644 nano/store/typed_iterator_templ.hpp 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/node/node.cpp b/nano/node/node.cpp index b3d5955511..41cdfae18c 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -755,7 +755,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; diff --git a/nano/node/wallet.cpp b/nano/node/wallet.cpp index 5364416907..387c479e93 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 ()); } @@ -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 @@ -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/store/CMakeLists.txt b/nano/store/CMakeLists.txt index 21306182fe..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 @@ -51,6 +52,8 @@ add_library( rocksdb/version.hpp tables.hpp transaction.hpp + typed_iterator.hpp + typed_iterator_templ.hpp version.hpp versioning.hpp account.cpp @@ -59,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 @@ -80,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 @@ -95,6 +100,7 @@ add_library( 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 bec1c3cd4a..5a460563f7 100644 --- a/nano/store/rocksdb/iterator.hpp +++ b/nano/store/rocksdb/iterator.hpp @@ -1,196 +1,60 @@ #pragma once -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include -#include -#include #include #include 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) - { - auto internals = rocksdb::tx (transaction_a); - auto 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, handle_a); - } - else if constexpr (std::is_same_v) - { - ptr->fill_cache = false; - return db->NewIterator (*ptr, handle_a); - } - else - { - static_assert (sizeof (V) == 0, "Missing variant handler for type V"); - } - }, - internals); - cursor.reset (iterator); - - 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; - - 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; - } + 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->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; +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 a739bc3254..fe6026fdfb 100644 --- a/nano/store/rocksdb/rocksdb.cpp +++ b/nano/store/rocksdb/rocksdb.cpp @@ -302,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) { diff --git a/nano/store/rocksdb/rocksdb.hpp b/nano/store/rocksdb/rocksdb.hpp index 493a26fff6..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; 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); +} +}