From 6a26366e8b72818e85b9ea8507e885db16576bd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Thu, 24 Oct 2024 19:16:21 +0200 Subject: [PATCH 1/9] Move random_pool test to a separate file --- nano/core_test/CMakeLists.txt | 1 + nano/core_test/random_pool.cpp | 48 ++++++++++++++++++++++++++++++++ nano/core_test/uint256_union.cpp | 43 ---------------------------- 3 files changed, 49 insertions(+), 43 deletions(-) create mode 100644 nano/core_test/random_pool.cpp diff --git a/nano/core_test/CMakeLists.txt b/nano/core_test/CMakeLists.txt index e5b47c9e43..6470ad2736 100644 --- a/nano/core_test/CMakeLists.txt +++ b/nano/core_test/CMakeLists.txt @@ -39,6 +39,7 @@ add_executable( optimistic_scheduler.cpp processing_queue.cpp processor_service.cpp + random_pool.cpp rep_crawler.cpp receivable.cpp peer_history.cpp diff --git a/nano/core_test/random_pool.cpp b/nano/core_test/random_pool.cpp new file mode 100644 index 0000000000..227ea87fcb --- /dev/null +++ b/nano/core_test/random_pool.cpp @@ -0,0 +1,48 @@ +#include +#include + +#include + +#include + +TEST (random_pool, multithreading) +{ + std::vector threads; + for (auto i = 0; i < 100; ++i) + { + threads.emplace_back ([] () { + nano::uint256_union number; + nano::random_pool::generate_block (number.bytes.data (), number.bytes.size ()); + }); + } + for (auto & i : threads) + { + i.join (); + } +} + +// Test that random 64bit numbers are within the given range +TEST (random_pool, generate_word64) +{ + int occurrences[10] = { 0 }; + for (auto i = 0; i < 1000; ++i) + { + auto random = nano::random_pool::generate_word64 (1, 9); + ASSERT_TRUE (random >= 1 && random <= 9); + occurrences[random] += 1; + } + + for (auto i = 1; i < 10; ++i) + { + ASSERT_GT (occurrences[i], 0); + } +} + +// Test random numbers > uint32 max +TEST (random_pool, generate_word64_big_number) +{ + uint64_t min = static_cast (std::numeric_limits::max ()) + 1; + uint64_t max = std::numeric_limits::max (); + auto big_random = nano::random_pool::generate_word64 (min, max); + ASSERT_GE (big_random, min); +} diff --git a/nano/core_test/uint256_union.cpp b/nano/core_test/uint256_union.cpp index 743d0fe1c7..7d939ad32a 100644 --- a/nano/core_test/uint256_union.cpp +++ b/nano/core_test/uint256_union.cpp @@ -1,4 +1,3 @@ -#include #include #include @@ -573,45 +572,3 @@ void check_operator_greater_than (Num lhs, Num rhs) ASSERT_FALSE (rhs > rhs); } } - -TEST (random_pool, multithreading) -{ - std::vector threads; - for (auto i = 0; i < 100; ++i) - { - threads.emplace_back ([] () { - nano::uint256_union number; - nano::random_pool::generate_block (number.bytes.data (), number.bytes.size ()); - }); - } - for (auto & i : threads) - { - i.join (); - } -} - -// Test that random 64bit numbers are within the given range -TEST (random_pool, generate_word64) -{ - int occurrences[10] = { 0 }; - for (auto i = 0; i < 1000; ++i) - { - auto random = nano::random_pool::generate_word64 (1, 9); - ASSERT_TRUE (random >= 1 && random <= 9); - occurrences[random] += 1; - } - - for (auto i = 1; i < 10; ++i) - { - ASSERT_GT (occurrences[i], 0); - } -} - -// Test random numbers > uint32 max -TEST (random_pool, generate_word64_big_number) -{ - uint64_t min = static_cast (std::numeric_limits::max ()) + 1; - uint64_t max = std::numeric_limits::max (); - auto big_random = nano::random_pool::generate_word64 (min, max); - ASSERT_GE (big_random, min); -} From 674d5af863e85e729e8edf4f617f818108cd2df6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Thu, 24 Oct 2024 19:23:08 +0200 Subject: [PATCH 2/9] Rename uint256 tests to numbers --- nano/core_test/CMakeLists.txt | 2 +- nano/core_test/{uint256_union.cpp => numbers.cpp} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename nano/core_test/{uint256_union.cpp => numbers.cpp} (100%) diff --git a/nano/core_test/CMakeLists.txt b/nano/core_test/CMakeLists.txt index 6470ad2736..1d01f6e796 100644 --- a/nano/core_test/CMakeLists.txt +++ b/nano/core_test/CMakeLists.txt @@ -35,6 +35,7 @@ add_executable( network_filter.cpp network_functions.cpp node.cpp + numbers.cpp object_stream.cpp optimistic_scheduler.cpp processing_queue.cpp @@ -55,7 +56,6 @@ add_executable( throttle.cpp toml.cpp timer.cpp - uint256_union.cpp unchecked_map.cpp utility.cpp vote_cache.cpp diff --git a/nano/core_test/uint256_union.cpp b/nano/core_test/numbers.cpp similarity index 100% rename from nano/core_test/uint256_union.cpp rename to nano/core_test/numbers.cpp From 7f5c394ca54f82a57d10ae29263e561b9f9288f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Thu, 24 Oct 2024 19:31:05 +0200 Subject: [PATCH 3/9] Complete missing testcase --- nano/core_test/numbers.cpp | 7 ++++++- nano/lib/numbers.hpp | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/nano/core_test/numbers.cpp b/nano/core_test/numbers.cpp index 7d939ad32a..1ffe78808b 100644 --- a/nano/core_test/numbers.cpp +++ b/nano/core_test/numbers.cpp @@ -1,5 +1,5 @@ +#include #include -#include #include @@ -453,6 +453,11 @@ TEST (uint256_union, operator_less_than) test_union_operator_less_than (); } +TEST (uint256_union, operator_greater_than) +{ + test_union_operator_greater_than (); +} + TEST (uint64_t, parse) { uint64_t value0 (1); diff --git a/nano/lib/numbers.hpp b/nano/lib/numbers.hpp index c637e1594b..71856e3b0d 100644 --- a/nano/lib/numbers.hpp +++ b/nano/lib/numbers.hpp @@ -13,6 +13,7 @@ namespace nano using uint128_t = boost::multiprecision::uint128_t; using uint256_t = boost::multiprecision::uint256_t; using uint512_t = boost::multiprecision::uint512_t; + // SI dividers nano::uint128_t const Knano_ratio = nano::uint128_t ("1000000000000000000000000000000000"); // 10^33 = 1000 nano nano::uint128_t const nano_ratio = nano::uint128_t ("1000000000000000000000000000000"); // 10^30 = 1 nano @@ -113,6 +114,10 @@ inline bool operator< (nano::uint256_union const & lhs, nano::uint256_union cons { return std::memcmp (lhs.bytes.data (), rhs.bytes.data (), 32) < 0; } +inline bool operator> (nano::uint256_union const & lhs, nano::uint256_union const & rhs) +{ + return std::memcmp (lhs.bytes.data (), rhs.bytes.data (), 32) > 0; +} static_assert (std::is_nothrow_move_constructible::value, "uint256_union should be noexcept MoveConstructible"); class link; From aefd7c18fbebf836e337d2831e52a2fc27451891 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Thu, 24 Oct 2024 20:02:30 +0200 Subject: [PATCH 4/9] Use spaceship comparisons --- nano/core_test/ledger.cpp | 2 +- nano/lib/numbers.cpp | 65 ------- nano/lib/numbers.hpp | 198 +++++++++++++++----- nano/node/bootstrap/bootstrap_bulk_pull.cpp | 4 +- nano/node/bootstrap/bootstrap_frontier.cpp | 2 +- nano/node/bootstrap/bootstrap_lazy.cpp | 2 +- nano/node/bootstrap_ascending/service.cpp | 2 +- nano/node/json_handler.cpp | 2 +- nano/qt/qt.cpp | 2 +- nano/rpc_test/rpc.cpp | 2 +- nano/secure/ledger.cpp | 2 +- 11 files changed, 159 insertions(+), 124 deletions(-) diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index 23d3d751b1..0dd6e786db 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -2406,7 +2406,7 @@ TEST (ledger, state_account) .work (*pool.generate (nano::dev::genesis->hash ())) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send1)); - ASSERT_EQ (nano::dev::genesis_key.pub, ledger.any.block_account (transaction, send1->hash ())); + ASSERT_EQ (nano::dev::genesis_key.pub, ledger.any.block_account (transaction, send1->hash ()).value ()); } TEST (ledger, state_send_receive) diff --git a/nano/lib/numbers.cpp b/nano/lib/numbers.cpp index f33d6f3cb3..b4bb819b71 100644 --- a/nano/lib/numbers.cpp +++ b/nano/lib/numbers.cpp @@ -286,11 +286,6 @@ nano::uint256_union::uint256_union (uint64_t value0) *this = nano::uint256_t (value0); } -bool nano::uint512_union::operator== (nano::uint512_union const & other_a) const -{ - return bytes == other_a.bytes; -} - nano::uint512_union::uint512_union (nano::uint256_union const & upper, nano::uint256_union const & lower) { uint256s[0] = upper; @@ -355,11 +350,6 @@ bool nano::uint512_union::decode_hex (std::string const & text) return error; } -bool nano::uint512_union::operator!= (nano::uint512_union const & other_a) const -{ - return !(*this == other_a); -} - nano::uint512_union & nano::uint512_union::operator^= (nano::uint512_union const & other_a) { uint256s[0] ^= other_a.uint256s[0]; @@ -446,26 +436,6 @@ nano::uint128_union::uint128_union (nano::uint128_t const & number_a) boost::multiprecision::export_bits (number_a, bytes.rbegin (), 8, false); } -bool nano::uint128_union::operator== (nano::uint128_union const & other_a) const -{ - return qwords[0] == other_a.qwords[0] && qwords[1] == other_a.qwords[1]; -} - -bool nano::uint128_union::operator!= (nano::uint128_union const & other_a) const -{ - return !(*this == other_a); -} - -bool nano::uint128_union::operator< (nano::uint128_union const & other_a) const -{ - return std::memcmp (bytes.data (), other_a.bytes.data (), 16) < 0; -} - -bool nano::uint128_union::operator> (nano::uint128_union const & other_a) const -{ - return std::memcmp (bytes.data (), other_a.bytes.data (), 16) > 0; -} - nano::uint128_t nano::uint128_union::number () const { nano::uint128_t result; @@ -811,36 +781,11 @@ std::string nano::hash_or_account::to_account () const return account.to_account (); } -nano::block_hash const & nano::hash_or_account::as_block_hash () const -{ - return hash; -} - -nano::account const & nano::hash_or_account::as_account () const -{ - return account; -} - -nano::hash_or_account::operator nano::uint256_union const & () const -{ - return raw; -} - nano::block_hash const & nano::root::previous () const { return hash; } -bool nano::hash_or_account::operator== (nano::hash_or_account const & hash_or_account_a) const -{ - return bytes == hash_or_account_a.bytes; -} - -bool nano::hash_or_account::operator!= (nano::hash_or_account const & hash_or_account_a) const -{ - return !(*this == hash_or_account_a); -} - std::string nano::to_string_hex (uint64_t const value_a) { std::stringstream stream; @@ -963,16 +908,6 @@ nano::public_key::operator nano::hash_or_account const & () const return reinterpret_cast (*this); } -bool nano::public_key::operator== (std::nullptr_t) const -{ - return bytes == null ().bytes; -} - -bool nano::public_key::operator!= (std::nullptr_t) const -{ - return !(*this == nullptr); -} - nano::block_hash::operator nano::link const & () const { return reinterpret_cast (*this); diff --git a/nano/lib/numbers.hpp b/nano/lib/numbers.hpp index 71856e3b0d..aca9a338cd 100644 --- a/nano/lib/numbers.hpp +++ b/nano/lib/numbers.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -23,29 +24,32 @@ class uint128_union { public: uint128_union () = default; + uint128_union (uint64_t); + uint128_union (nano::uint128_t const &); + /** * Decode from hex string * @warning Aborts at runtime if the input is invalid */ - uint128_union (std::string const &); - uint128_union (uint64_t); - uint128_union (nano::uint128_t const &); - bool operator== (nano::uint128_union const &) const; - bool operator!= (nano::uint128_union const &) const; - bool operator< (nano::uint128_union const &) const; - bool operator> (nano::uint128_union const &) const; + explicit uint128_union (std::string const &); + void encode_hex (std::string &) const; bool decode_hex (std::string const &); void encode_dec (std::string &) const; bool decode_dec (std::string const &, bool = false); bool decode_dec (std::string const &, nano::uint128_t); + std::string format_balance (nano::uint128_t scale, int precision, bool group_digits) const; std::string format_balance (nano::uint128_t scale, int precision, bool group_digits, std::locale const & locale) const; + nano::uint128_t number () const; void clear (); bool is_zero () const; + std::string to_string () const; std::string to_string_dec () const; + +public: union { std::array bytes; @@ -53,6 +57,24 @@ class uint128_union std::array dwords; std::array qwords; }; + +public: // Keep operators inlined + std::strong_ordering operator<=> (nano::uint128_union const & other) const + { + return std::memcmp (bytes.data (), other.bytes.data (), 16) <=> 0; + }; + bool operator== (nano::uint128_union const & other) const + { + return *this <=> other == 0; + } + operator nano::uint128_t () const + { + return number (); + } + uint128_union const & as_union () const + { + return *this; + } }; static_assert (std::is_nothrow_move_constructible::value, "uint128_union should be noexcept MoveConstructible"); @@ -62,37 +84,48 @@ class amount : public uint128_union public: using uint128_union::uint128_union; + auto operator<=> (nano::amount const & other) const + { + return uint128_union::operator<=> (other); + } operator nano::uint128_t () const { return number (); } }; + class raw_key; class uint256_union { public: uint256_union () = default; + uint256_union (uint64_t); + uint256_union (uint256_t const &); + /** * Decode from hex string * @warning Aborts at runtime if the input is invalid */ explicit uint256_union (std::string const &); - uint256_union (uint64_t); - uint256_union (nano::uint256_t const &); + void encrypt (nano::raw_key const &, nano::raw_key const &, uint128_union const &); - uint256_union & operator^= (nano::uint256_union const &); - uint256_union operator^ (nano::uint256_union const &) const; + + uint256_union & operator^= (uint256_union const &); + uint256_union operator^ (uint256_union const &) const; + void encode_hex (std::string &) const; bool decode_hex (std::string const &); void encode_dec (std::string &) const; bool decode_dec (std::string const &); + nano::uint256_t number () const; void clear (); bool is_zero () const; + std::string to_string () const; - nano::uint256_t number () const; +public: union { std::array bytes; @@ -101,23 +134,25 @@ class uint256_union std::array qwords; std::array owords; }; + +public: // Keep operators inlined + std::strong_ordering operator<=> (nano::uint256_union const & other) const + { + return std::memcmp (bytes.data (), other.bytes.data (), 32) <=> 0; + }; + bool operator== (nano::uint256_union const & other) const + { + return *this <=> other == 0; + } + operator nano::uint256_t () const + { + return number (); + } + uint256_union const & as_union () const + { + return *this; + } }; -inline bool operator== (nano::uint256_union const & lhs, nano::uint256_union const & rhs) -{ - return lhs.bytes == rhs.bytes; -} -inline bool operator!= (nano::uint256_union const & lhs, nano::uint256_union const & rhs) -{ - return !(lhs == rhs); -} -inline bool operator< (nano::uint256_union const & lhs, nano::uint256_union const & rhs) -{ - return std::memcmp (lhs.bytes.data (), rhs.bytes.data (), 32) < 0; -} -inline bool operator> (nano::uint256_union const & lhs, nano::uint256_union const & rhs) -{ - return std::memcmp (lhs.bytes.data (), rhs.bytes.data (), 32) > 0; -} static_assert (std::is_nothrow_move_constructible::value, "uint256_union should be noexcept MoveConstructible"); class link; @@ -129,9 +164,24 @@ class block_hash final : public uint256_union { public: using uint256_union::uint256_union; + operator nano::link const & () const; operator nano::root const & () const; operator nano::hash_or_account const & () const; + +public: // Keep operators inlined + auto operator<=> (nano::block_hash const & other) const + { + return uint256_union::operator<=> (other); + } + bool operator== (nano::block_hash const & other) const + { + return *this <=> other == 0; + } + operator nano::uint256_t () const + { + return number (); + } }; class public_key final : public uint256_union @@ -152,8 +202,24 @@ class public_key final : public uint256_union operator nano::link const & () const; operator nano::root const & () const; operator nano::hash_or_account const & () const; - bool operator== (std::nullptr_t) const; - bool operator!= (std::nullptr_t) const; + +public: // Keep operators inlined + auto operator<=> (nano::public_key const & other) const + { + return uint256_union::operator<=> (other); + } + bool operator== (nano::public_key const & other) const + { + return *this <=> other == 0; + } + bool operator== (std::nullptr_t) const + { + return *this == null (); + } + operator nano::uint256_t () const + { + return number (); + } }; class wallet_id : public uint256_union @@ -172,19 +238,13 @@ class hash_or_account bool is_zero () const; void clear (); + std::string to_string () const; bool decode_hex (std::string const &); bool decode_account (std::string const &); std::string to_account () const; - nano::account const & as_account () const; - nano::block_hash const & as_block_hash () const; - - operator nano::uint256_union const & () const; - - bool operator== (nano::hash_or_account const &) const; - bool operator!= (nano::hash_or_account const &) const; - +public: union { std::array bytes; @@ -192,6 +252,32 @@ class hash_or_account nano::account account; nano::block_hash hash; }; + +public: // Keep operators inlined + auto operator<=> (nano::hash_or_account const & other) const + { + return raw <=> other.raw; + }; + bool operator== (nano::hash_or_account const & other) const + { + return *this <=> other == 0; + } + operator nano::uint256_t () const + { + return raw.number (); + } + operator nano::uint256_union () const + { + return raw; + } + nano::account const & as_account () const + { + return account; + } + nano::block_hash const & as_block_hash () const + { + return hash; + } }; // A link can either be a destination account or source hash @@ -218,22 +304,27 @@ class raw_key final : public uint256_union ~raw_key (); void decrypt (nano::uint256_union const &, nano::raw_key const &, uint128_union const &); }; + class uint512_union { public: uint512_union () = default; uint512_union (nano::uint256_union const &, nano::uint256_union const &); uint512_union (nano::uint512_t const &); - bool operator== (nano::uint512_union const &) const; - bool operator!= (nano::uint512_union const &) const; + nano::uint512_union & operator^= (nano::uint512_union const &); + void encode_hex (std::string &) const; bool decode_hex (std::string const &); + void clear (); bool is_zero () const; + nano::uint512_t number () const; + std::string to_string () const; +public: union { std::array bytes; @@ -241,6 +332,24 @@ class uint512_union std::array qwords; std::array uint256s; }; + +public: // Keep operators inlined + std::strong_ordering operator<=> (nano::uint512_union const & other) const + { + return std::memcmp (bytes.data (), other.bytes.data (), 64) <=> 0; + }; + bool operator== (nano::uint512_union const & other) const + { + return *this <=> other == 0; + } + operator nano::uint512_t () const + { + return number (); + } + uint512_union const & as_union () const + { + return *this; + } }; static_assert (std::is_nothrow_move_constructible::value, "uint512_union should be noexcept MoveConstructible"); @@ -381,15 +490,6 @@ struct hash<::nano::qualified_root> return hash<::nano::uint512_union> () (data_a); } }; - -template <> -struct equal_to> -{ - bool operator() (std::reference_wrapper<::nano::block_hash const> const & lhs, std::reference_wrapper<::nano::block_hash const> const & rhs) const - { - return lhs.get () == rhs.get (); - } -}; } namespace boost diff --git a/nano/node/bootstrap/bootstrap_bulk_pull.cpp b/nano/node/bootstrap/bootstrap_bulk_pull.cpp index a4967bf35c..892e28691d 100644 --- a/nano/node/bootstrap/bootstrap_bulk_pull.cpp +++ b/nano/node/bootstrap/bootstrap_bulk_pull.cpp @@ -177,7 +177,7 @@ void nano::bulk_pull_client::received_block (boost::system::error_code ec, std:: // Is block expected? bool block_expected (false); // Unconfirmed head is used only for lazy destinations if legacy bootstrap is not available, see nano::bootstrap_attempt::lazy_destinations_increment (...) - bool unconfirmed_account_head = node->flags.disable_legacy_bootstrap && pull_blocks == 0 && pull.retry_limit <= node->network_params.bootstrap.lazy_retry_limit && (expected == pull.account_or_head.as_block_hash ()) && (block->account_field () == pull.account_or_head.as_account ()); + bool unconfirmed_account_head = node->flags.disable_legacy_bootstrap && pull_blocks == 0 && pull.retry_limit <= node->network_params.bootstrap.lazy_retry_limit && (expected == pull.account_or_head.as_block_hash ()) && (block->account_field ().value_or (0) == pull.account_or_head.as_account ()); if (hash == expected || unconfirmed_account_head) { expected = block->previous (); @@ -394,7 +394,7 @@ void nano::bulk_pull_server::set_current_end () if (!request->end.is_zero ()) { auto account (node->ledger.any.block_account (transaction, request->end)); - if (account != request->start.as_account ()) + if (account.value_or (0) != request->start.as_account ()) { node->logger.debug (nano::log::type::bulk_pull_server, "Request for block that is not on account chain: {} not on {}", request->end.to_string (), request->start.to_account ()); diff --git a/nano/node/bootstrap/bootstrap_frontier.cpp b/nano/node/bootstrap/bootstrap_frontier.cpp index db696dbd61..d0ad8276a1 100644 --- a/nano/node/bootstrap/bootstrap_frontier.cpp +++ b/nano/node/bootstrap/bootstrap_frontier.cpp @@ -22,7 +22,7 @@ void nano::frontier_req_client::run (nano::account const & start_account_a, uint return; } nano::frontier_req request{ node->network_params.network }; - request.start = (start_account_a.is_zero () || start_account_a.number () == std::numeric_limits::max ()) ? start_account_a : start_account_a.number () + 1; + request.start = (start_account_a.is_zero () || start_account_a.number () == std::numeric_limits::max ()) ? start_account_a.number () : start_account_a.number () + 1; request.age = frontiers_age_a; request.count = count_a; current = start_account_a; diff --git a/nano/node/bootstrap/bootstrap_lazy.cpp b/nano/node/bootstrap/bootstrap_lazy.cpp index 30661abd7a..439aa0ced0 100644 --- a/nano/node/bootstrap/bootstrap_lazy.cpp +++ b/nano/node/bootstrap/bootstrap_lazy.cpp @@ -293,7 +293,7 @@ bool nano::bootstrap_attempt_lazy::process_block_lazy (std::shared_ptrsource_field () && !node->block_or_pruned_exists (block_a->source_field ().value ()) && block_a->source_field ().value () != node->network_params.ledger.genesis->account ()) + if (block_a->source_field () && !node->block_or_pruned_exists (block_a->source_field ().value ()) && block_a->source_field ().value () != node->network_params.ledger.genesis->account ().as_union ()) { lazy_add (block_a->source_field ().value (), retry_limit); } diff --git a/nano/node/bootstrap_ascending/service.cpp b/nano/node/bootstrap_ascending/service.cpp index 776f0b3d8e..661072b448 100644 --- a/nano/node/bootstrap_ascending/service.cpp +++ b/nano/node/bootstrap_ascending/service.cpp @@ -789,7 +789,7 @@ nano::bootstrap_ascending::service::verify_result nano::bootstrap_ascending::ser case query_type::blocks_by_account: { // Open & state blocks always contain account field - if (first->account_field () != tag.start.as_account ()) + if (first->account_field ().value_or (0) != tag.start.as_account ()) { // TODO: Stat & log return verify_result::invalid; diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index 591ce4b997..1091f38283 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -2462,7 +2462,7 @@ class history_visitor : public nano::block_visitor // Report opens as a receive tree.put ("type", "receive"); } - if (block_a.hashables.source != handler.node.ledger.constants.genesis->account ()) + if (block_a.hashables.source != handler.node.ledger.constants.genesis->account ().as_union ()) { bool error_or_pruned (false); auto amount = handler.node.ledger.any.block_amount (transaction, hash); diff --git a/nano/qt/qt.cpp b/nano/qt/qt.cpp index 48466d012a..7a513e8b4a 100644 --- a/nano/qt/qt.cpp +++ b/nano/qt/qt.cpp @@ -555,7 +555,7 @@ class short_text_visitor : public nano::block_visitor void open_block (nano::open_block const & block_a) { type = "Receive"; - if (block_a.hashables.source != ledger.constants.genesis->account ()) + if (block_a.hashables.source != ledger.constants.genesis->account ().as_union ()) { auto account_l = ledger.any.block_account (transaction, block_a.hashables.source); auto amount_l = ledger.any.block_amount (transaction, block_a.hash ()); diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index 309290dc1d..206a550922 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -2628,7 +2628,7 @@ TEST (rpc, wallet_frontiers) frontiers.push_back (nano::account (i->second.get (""))); } ASSERT_EQ (1, frontiers.size ()); - ASSERT_EQ (node->latest (nano::dev::genesis_key.pub), frontiers[0]); + ASSERT_EQ (node->latest (nano::dev::genesis_key.pub), frontiers[0].as_union ()); } TEST (rpc, work_validate) diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index ec5aa666da..d2857974de 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -1093,7 +1093,7 @@ class dependent_block_visitor : public nano::block_visitor } void open_block (nano::open_block const & block_a) override { - if (block_a.source_field ().value () != ledger.constants.genesis->account ()) + if (block_a.source_field ().value () != ledger.constants.genesis->account ().as_union ()) { result[0] = block_a.source_field ().value (); } From 57d263df1eee6d9a396a5908d2023f36307b74ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Thu, 24 Oct 2024 22:46:33 +0200 Subject: [PATCH 5/9] Avoid reinterpret casts --- nano/lib/numbers.cpp | 42 +++++---------------------------- nano/lib/numbers.hpp | 56 ++++++++++++++++++++++++++++++++++++-------- 2 files changed, 52 insertions(+), 46 deletions(-) diff --git a/nano/lib/numbers.cpp b/nano/lib/numbers.cpp index b4bb819b71..d5cb7f0c97 100644 --- a/nano/lib/numbers.cpp +++ b/nano/lib/numbers.cpp @@ -747,7 +747,12 @@ nano::hash_or_account::hash_or_account () : } nano::hash_or_account::hash_or_account (uint64_t value_a) : - raw (value_a) + raw{ value_a } +{ +} + +nano::hash_or_account::hash_or_account (uint256_union const & value_a) : + raw{ value_a } { } @@ -781,11 +786,6 @@ std::string nano::hash_or_account::to_account () const return account.to_account (); } -nano::block_hash const & nano::root::previous () const -{ - return hash; -} - std::string nano::to_string_hex (uint64_t const value_a) { std::stringstream stream; @@ -892,33 +892,3 @@ double nano::difficulty::to_multiplier (uint64_t const difficulty_a, uint64_t co #ifdef _WIN32 #pragma warning(pop) #endif - -nano::public_key::operator nano::link const & () const -{ - return reinterpret_cast (*this); -} - -nano::public_key::operator nano::root const & () const -{ - return reinterpret_cast (*this); -} - -nano::public_key::operator nano::hash_or_account const & () const -{ - return reinterpret_cast (*this); -} - -nano::block_hash::operator nano::link const & () const -{ - return reinterpret_cast (*this); -} - -nano::block_hash::operator nano::root const & () const -{ - return reinterpret_cast (*this); -} - -nano::block_hash::operator nano::hash_or_account const & () const -{ - return reinterpret_cast (*this); -} diff --git a/nano/lib/numbers.hpp b/nano/lib/numbers.hpp index aca9a338cd..82b4786e85 100644 --- a/nano/lib/numbers.hpp +++ b/nano/lib/numbers.hpp @@ -165,10 +165,6 @@ class block_hash final : public uint256_union public: using uint256_union::uint256_union; - operator nano::link const & () const; - operator nano::root const & () const; - operator nano::hash_or_account const & () const; - public: // Keep operators inlined auto operator<=> (nano::block_hash const & other) const { @@ -182,6 +178,18 @@ class block_hash final : public uint256_union { return number (); } + operator nano::link () const + { + return nano::link{ *this }; + } + operator nano::root () const + { + return nano::root{ *this }; + } + operator nano::hash_or_account () const + { + return nano::hash_or_account{ *this }; + } }; class public_key final : public uint256_union @@ -234,7 +242,8 @@ class hash_or_account { public: hash_or_account (); - hash_or_account (uint64_t value_a); + hash_or_account (uint64_t); + explicit hash_or_account (uint256_union const &); bool is_zero () const; void clear (); @@ -278,6 +287,10 @@ class hash_or_account { return hash; } + nano::uint256_union const & as_union () const + { + return raw; + } }; // A link can either be a destination account or source hash @@ -285,6 +298,16 @@ class link final : public hash_or_account { public: using hash_or_account::hash_or_account; + +public: // Keep operators inlined + auto operator<=> (nano::link const & other) const + { + return hash_or_account::operator<=> (other); + } + bool operator== (nano::link const & other) const + { + return *this <=> other == 0; + } }; // A root can either be an open block hash or a previous hash @@ -293,7 +316,20 @@ class root final : public hash_or_account public: using hash_or_account::hash_or_account; - nano::block_hash const & previous () const; + nano::block_hash const & previous () const + { + return hash; + } + +public: // Keep operators inlined + auto operator<=> (nano::root const & other) const + { + return hash_or_account::operator<=> (other); + } + bool operator== (nano::root const & other) const + { + return *this <=> other == 0; + } }; // The seed or private key @@ -364,13 +400,13 @@ class qualified_root : public uint512_union public: using uint512_union::uint512_union; - nano::root const & root () const + nano::root root () const { - return reinterpret_cast (uint256s[0]); + return nano::root{ uint256s[0] }; } - nano::block_hash const & previous () const + nano::block_hash previous () const { - return reinterpret_cast (uint256s[1]); + return nano::block_hash{ uint256s[1] }; } }; From e246aa4e40da715b27d76564dad431ec3255773d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Thu, 24 Oct 2024 23:18:46 +0200 Subject: [PATCH 6/9] Keep commonly used functions inline --- nano/lib/numbers.cpp | 125 ---------------------------------------- nano/lib/numbers.hpp | 133 +++++++++++++++++++++++++++++++++---------- 2 files changed, 104 insertions(+), 154 deletions(-) diff --git a/nano/lib/numbers.cpp b/nano/lib/numbers.cpp index d5cb7f0c97..3d2e888236 100644 --- a/nano/lib/numbers.cpp +++ b/nano/lib/numbers.cpp @@ -61,11 +61,6 @@ std::string nano::public_key::to_account () const return result; } -nano::public_key::public_key () : - uint256_union{ 0 } -{ -} - nano::public_key const & nano::public_key::null () { return nano::hardened_constants::get ().not_an_account; @@ -143,12 +138,6 @@ bool nano::public_key::decode_account (std::string const & source_a) return error; } -nano::uint256_union::uint256_union (nano::uint256_t const & number_a) -{ - bytes.fill (0); - boost::multiprecision::export_bits (number_a, bytes.rbegin (), 8, false); -} - // Construct a uint256_union = AES_ENC_CTR (cleartext, key, iv) void nano::uint256_union::encrypt (nano::raw_key const & cleartext, nano::raw_key const & key, uint128_union const & iv) { @@ -157,11 +146,6 @@ void nano::uint256_union::encrypt (nano::raw_key const & cleartext, nano::raw_ke enc.ProcessData (bytes.data (), cleartext.bytes.data (), sizeof (cleartext.bytes)); } -bool nano::uint256_union::is_zero () const -{ - return qwords[0] == 0 && qwords[1] == 0 && qwords[2] == 0 && qwords[3] == 0; -} - std::string nano::uint256_union::to_string () const { std::string result; @@ -193,22 +177,9 @@ nano::uint256_union nano::uint256_union::operator^ (nano::uint256_union const & nano::uint256_union::uint256_union (std::string const & hex_a) { auto error (decode_hex (hex_a)); - release_assert (!error); } -void nano::uint256_union::clear () -{ - qwords.fill (0); -} - -nano::uint256_t nano::uint256_union::number () const -{ - nano::uint256_t result; - boost::multiprecision::import_bits (result, bytes.begin (), bytes.end ()); - return result; -} - void nano::uint256_union::encode_hex (std::string & text) const { debug_assert (text.empty ()); @@ -281,41 +252,6 @@ bool nano::uint256_union::decode_dec (std::string const & text) return error; } -nano::uint256_union::uint256_union (uint64_t value0) -{ - *this = nano::uint256_t (value0); -} - -nano::uint512_union::uint512_union (nano::uint256_union const & upper, nano::uint256_union const & lower) -{ - uint256s[0] = upper; - uint256s[1] = lower; -} - -nano::uint512_union::uint512_union (nano::uint512_t const & number_a) -{ - bytes.fill (0); - boost::multiprecision::export_bits (number_a, bytes.rbegin (), 8, false); -} - -bool nano::uint512_union::is_zero () const -{ - return qwords[0] == 0 && qwords[1] == 0 && qwords[2] == 0 && qwords[3] == 0 - && qwords[4] == 0 && qwords[5] == 0 && qwords[6] == 0 && qwords[7] == 0; -} - -void nano::uint512_union::clear () -{ - bytes.fill (0); -} - -nano::uint512_t nano::uint512_union::number () const -{ - nano::uint512_t result; - boost::multiprecision::import_bits (result, bytes.begin (), bytes.end ()); - return result; -} - void nano::uint512_union::encode_hex (std::string & text) const { debug_assert (text.empty ()); @@ -350,13 +286,6 @@ bool nano::uint512_union::decode_hex (std::string const & text) return error; } -nano::uint512_union & nano::uint512_union::operator^= (nano::uint512_union const & other_a) -{ - uint256s[0] ^= other_a.uint256s[0]; - uint256s[1] ^= other_a.uint256s[1]; - return *this; -} - std::string nano::uint512_union::to_string () const { std::string result; @@ -421,28 +350,9 @@ bool nano::validate_message (nano::public_key const & public_key, nano::uint256_ nano::uint128_union::uint128_union (std::string const & string_a) { auto error (decode_hex (string_a)); - release_assert (!error); } -nano::uint128_union::uint128_union (uint64_t value_a) -{ - *this = nano::uint128_t (value_a); -} - -nano::uint128_union::uint128_union (nano::uint128_t const & number_a) -{ - bytes.fill (0); - boost::multiprecision::export_bits (number_a, bytes.rbegin (), 8, false); -} - -nano::uint128_t nano::uint128_union::number () const -{ - nano::uint128_t result; - boost::multiprecision::import_bits (result, bytes.begin (), bytes.end ()); - return result; -} - void nano::uint128_union::encode_hex (std::string & text) const { debug_assert (text.empty ()); @@ -717,16 +627,6 @@ std::string nano::uint128_union::format_balance (nano::uint128_t scale, int prec return ::format_balance (number (), scale, precision, group_digits, thousands_sep, decimal_point, grouping); } -void nano::uint128_union::clear () -{ - qwords.fill (0); -} - -bool nano::uint128_union::is_zero () const -{ - return qwords[0] == 0 && qwords[1] == 0; -} - std::string nano::uint128_union::to_string () const { std::string result; @@ -741,31 +641,6 @@ std::string nano::uint128_union::to_string_dec () const return result; } -nano::hash_or_account::hash_or_account () : - account{} -{ -} - -nano::hash_or_account::hash_or_account (uint64_t value_a) : - raw{ value_a } -{ -} - -nano::hash_or_account::hash_or_account (uint256_union const & value_a) : - raw{ value_a } -{ -} - -bool nano::hash_or_account::is_zero () const -{ - return raw.is_zero (); -} - -void nano::hash_or_account::clear () -{ - raw.clear (); -} - bool nano::hash_or_account::decode_hex (std::string const & text_a) { return raw.decode_hex (text_a); diff --git a/nano/lib/numbers.hpp b/nano/lib/numbers.hpp index 82b4786e85..2db23118b2 100644 --- a/nano/lib/numbers.hpp +++ b/nano/lib/numbers.hpp @@ -24,8 +24,13 @@ class uint128_union { public: uint128_union () = default; - uint128_union (uint64_t); - uint128_union (nano::uint128_t const &); + uint128_union (uint64_t value) : + uint128_union (nano::uint128_t{ value }){}; + uint128_union (nano::uint128_t const & value) + { + bytes.fill (0); + boost::multiprecision::export_bits (value, bytes.rbegin (), 8, false); + } /** * Decode from hex string @@ -42,9 +47,21 @@ class uint128_union std::string format_balance (nano::uint128_t scale, int precision, bool group_digits) const; std::string format_balance (nano::uint128_t scale, int precision, bool group_digits, std::locale const & locale) const; - nano::uint128_t number () const; - void clear (); - bool is_zero () const; + void clear () + { + qwords.fill (0); + } + bool is_zero () const + { + return qwords[0] == 0 && qwords[1] == 0; + } + + nano::uint128_t number () const + { + nano::uint128_t result; + boost::multiprecision::import_bits (result, bytes.begin (), bytes.end ()); + return result; + } std::string to_string () const; std::string to_string_dec () const; @@ -100,8 +117,13 @@ class uint256_union { public: uint256_union () = default; - uint256_union (uint64_t); - uint256_union (uint256_t const &); + uint256_union (uint64_t value) : + uint256_union (nano::uint256_t{ value }){}; + uint256_union (uint256_t const & value) + { + bytes.fill (0); + boost::multiprecision::export_bits (value, bytes.rbegin (), 8, false); + } /** * Decode from hex string @@ -119,9 +141,21 @@ class uint256_union void encode_dec (std::string &) const; bool decode_dec (std::string const &); - nano::uint256_t number () const; - void clear (); - bool is_zero () const; + void clear () + { + qwords.fill (0); + } + bool is_zero () const + { + return owords[0].is_zero () && owords[1].is_zero (); + } + + nano::uint256_t number () const + { + nano::uint256_t result; + boost::multiprecision::import_bits (result, bytes.begin (), bytes.end ()); + return result; + } std::string to_string () const; @@ -197,19 +231,17 @@ class public_key final : public uint256_union public: using uint256_union::uint256_union; - public_key (); + public_key () : + uint256_union{ 0 } {}; static const public_key & null (); - std::string to_node_id () const; - bool decode_node_id (std::string const & source_a); + bool decode_node_id (std::string const &); void encode_account (std::string &) const; - std::string to_account () const; bool decode_account (std::string const &); - operator nano::link const & () const; - operator nano::root const & () const; - operator nano::hash_or_account const & () const; + std::string to_node_id () const; + std::string to_account () const; public: // Keep operators inlined auto operator<=> (nano::public_key const & other) const @@ -228,6 +260,18 @@ class public_key final : public uint256_union { return number (); } + operator nano::link () const + { + return nano::link{ *this }; + } + operator nano::root () const + { + return nano::root{ *this }; + } + operator nano::hash_or_account () const + { + return nano::hash_or_account{ *this }; + } }; class wallet_id : public uint256_union @@ -241,16 +285,26 @@ using account = public_key; class hash_or_account { public: - hash_or_account (); - hash_or_account (uint64_t); - explicit hash_or_account (uint256_union const &); + hash_or_account () : + account{} {}; + hash_or_account (uint64_t value) : + raw{ value } {}; + explicit hash_or_account (uint256_union const & value) : + raw{ value } {}; - bool is_zero () const; - void clear (); + void clear () + { + raw.clear (); + } + bool is_zero () const + { + return raw.is_zero (); + } - std::string to_string () const; bool decode_hex (std::string const &); bool decode_account (std::string const &); + + std::string to_string () const; std::string to_account () const; public: @@ -345,18 +399,39 @@ class uint512_union { public: uint512_union () = default; - uint512_union (nano::uint256_union const &, nano::uint256_union const &); - uint512_union (nano::uint512_t const &); + uint512_union (nano::uint256_union const & upper, nano::uint256_union const & lower) : + uint256s{ upper, lower } {}; + uint512_union (nano::uint512_t const & value) + { + bytes.fill (0); + boost::multiprecision::export_bits (value, bytes.rbegin (), 8, false); + } - nano::uint512_union & operator^= (nano::uint512_union const &); + nano::uint512_union & operator^= (nano::uint512_union const & other) + { + uint256s[0] ^= other.uint256s[0]; + uint256s[1] ^= other.uint256s[1]; + return *this; + } void encode_hex (std::string &) const; bool decode_hex (std::string const &); - void clear (); - bool is_zero () const; + void clear () + { + bytes.fill (0); + } + bool is_zero () const + { + return uint256s[0].is_zero () && uint256s[1].is_zero (); + } - nano::uint512_t number () const; + nano::uint512_t number () const + { + nano::uint512_t result; + boost::multiprecision::import_bits (result, bytes.begin (), bytes.end ()); + return result; + } std::string to_string () const; From ffd46598ad15a0968f3579455c40cb9170584068 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Fri, 25 Oct 2024 13:01:41 +0200 Subject: [PATCH 7/9] Fix dangling reference returns --- nano/lib/blocks.cpp | 10 +++--- nano/lib/blocks.hpp | 12 +++---- nano/lib/numbers.cpp | 7 +++++ nano/lib/numbers.hpp | 52 ++++++++++--------------------- nano/node/bootstrap/bootstrap.cpp | 6 ++-- 5 files changed, 38 insertions(+), 49 deletions(-) diff --git a/nano/lib/blocks.cpp b/nano/lib/blocks.cpp index a4c21b8678..4fc527eb0d 100644 --- a/nano/lib/blocks.cpp +++ b/nano/lib/blocks.cpp @@ -609,7 +609,7 @@ std::optional nano::send_block::destination_field () const return hashables.destination; } -nano::root const & nano::send_block::root () const +nano::root nano::send_block::root () const { return hashables.previous; } @@ -899,7 +899,7 @@ std::optional nano::open_block::source_field () const return hashables.source; } -nano::root const & nano::open_block::root () const +nano::root nano::open_block::root () const { return hashables.account; } @@ -1165,7 +1165,7 @@ bool nano::change_block::valid_predecessor (nano::block const & block_a) const return result; } -nano::root const & nano::change_block::root () const +nano::root nano::change_block::root () const { return hashables.previous; } @@ -1482,7 +1482,7 @@ bool nano::state_block::valid_predecessor (nano::block const & block_a) const return true; } -nano::root const & nano::state_block::root () const +nano::root nano::state_block::root () const { if (!hashables.previous.is_zero ()) { @@ -1836,7 +1836,7 @@ std::optional nano::receive_block::source_field () const return hashables.source; } -nano::root const & nano::receive_block::root () const +nano::root nano::receive_block::root () const { return hashables.previous; } diff --git a/nano/lib/blocks.hpp b/nano/lib/blocks.hpp index 1ddb8302dd..2312855cb6 100644 --- a/nano/lib/blocks.hpp +++ b/nano/lib/blocks.hpp @@ -33,7 +33,7 @@ class block virtual uint64_t block_work () const = 0; virtual void block_work_set (uint64_t) = 0; // Previous block or account number for open blocks - virtual nano::root const & root () const = 0; + virtual nano::root root () const = 0; // Qualified root value based on previous() and root() virtual nano::qualified_root qualified_root () const; virtual void serialize (nano::stream &) const = 0; @@ -123,7 +123,7 @@ class send_block : public nano::block virtual ~send_block () = default; uint64_t block_work () const override; void block_work_set (uint64_t) override; - nano::root const & root () const override; + nano::root root () const override; void serialize (nano::stream &) const override; bool deserialize (nano::stream &); void serialize_json (std::string &, bool = false) const override; @@ -177,7 +177,7 @@ class receive_block : public nano::block virtual ~receive_block () = default; uint64_t block_work () const override; void block_work_set (uint64_t) override; - nano::root const & root () const override; + nano::root root () const override; void serialize (nano::stream &) const override; bool deserialize (nano::stream &); void serialize_json (std::string &, bool = false) const override; @@ -232,7 +232,7 @@ class open_block : public nano::block virtual ~open_block () = default; uint64_t block_work () const override; void block_work_set (uint64_t) override; - nano::root const & root () const override; + nano::root root () const override; void serialize (nano::stream &) const override; bool deserialize (nano::stream &); void serialize_json (std::string &, bool = false) const override; @@ -287,7 +287,7 @@ class change_block : public nano::block virtual ~change_block () = default; uint64_t block_work () const override; void block_work_set (uint64_t) override; - nano::root const & root () const override; + nano::root root () const override; void serialize (nano::stream &) const override; bool deserialize (nano::stream &); void serialize_json (std::string &, bool = false) const override; @@ -353,7 +353,7 @@ class state_block : public nano::block virtual ~state_block () = default; uint64_t block_work () const override; void block_work_set (uint64_t) override; - nano::root const & root () const override; + nano::root root () const override; void serialize (nano::stream &) const override; bool deserialize (nano::stream &); void serialize_json (std::string &, bool = false) const override; diff --git a/nano/lib/numbers.cpp b/nano/lib/numbers.cpp index 3d2e888236..916329b673 100644 --- a/nano/lib/numbers.cpp +++ b/nano/lib/numbers.cpp @@ -735,6 +735,13 @@ std::ostream & nano::operator<< (std::ostream & os, const uint512_union & val) return os; } +std::ostream & nano::operator<< (std::ostream & os, const hash_or_account & val) +{ + // TODO: Replace with streaming implementation + os << val.to_string (); + return os; +} + #ifdef _WIN32 #pragma warning(push) #pragma warning(disable : 4146) // warning C4146: unary minus operator applied to unsigned type, result still unsigned diff --git a/nano/lib/numbers.hpp b/nano/lib/numbers.hpp index 2db23118b2..88e4de560d 100644 --- a/nano/lib/numbers.hpp +++ b/nano/lib/numbers.hpp @@ -189,10 +189,6 @@ class uint256_union }; static_assert (std::is_nothrow_move_constructible::value, "uint256_union should be noexcept MoveConstructible"); -class link; -class root; -class hash_or_account; - // All keys and hashes are 256 bit. class block_hash final : public uint256_union { @@ -212,18 +208,6 @@ class block_hash final : public uint256_union { return number (); } - operator nano::link () const - { - return nano::link{ *this }; - } - operator nano::root () const - { - return nano::root{ *this }; - } - operator nano::hash_or_account () const - { - return nano::hash_or_account{ *this }; - } }; class public_key final : public uint256_union @@ -260,18 +244,6 @@ class public_key final : public uint256_union { return number (); } - operator nano::link () const - { - return nano::link{ *this }; - } - operator nano::root () const - { - return nano::root{ *this }; - } - operator nano::hash_or_account () const - { - return nano::hash_or_account{ *this }; - } }; class wallet_id : public uint256_union @@ -289,7 +261,7 @@ class hash_or_account account{} {}; hash_or_account (uint64_t value) : raw{ value } {}; - explicit hash_or_account (uint256_union const & value) : + hash_or_account (uint256_union const & value) : raw{ value } {}; void clear () @@ -325,11 +297,11 @@ class hash_or_account { return *this <=> other == 0; } - operator nano::uint256_t () const + explicit operator nano::uint256_t () const { return raw.number (); } - operator nano::uint256_union () const + explicit operator nano::uint256_union () const { return raw; } @@ -399,13 +371,13 @@ class uint512_union { public: uint512_union () = default; - uint512_union (nano::uint256_union const & upper, nano::uint256_union const & lower) : - uint256s{ upper, lower } {}; uint512_union (nano::uint512_t const & value) { bytes.fill (0); boost::multiprecision::export_bits (value, bytes.rbegin (), 8, false); } + uint512_union (nano::uint256_union const & upper, nano::uint256_union const & lower) : + uint256s{ upper, lower } {}; nano::uint512_union & operator^= (nano::uint512_union const & other) { @@ -473,7 +445,11 @@ class signature : public uint512_union class qualified_root : public uint512_union { public: - using uint512_union::uint512_union; + qualified_root () = default; + qualified_root (nano::root const & root, nano::block_hash const & previous) : + uint512_union{ root.as_union (), previous.as_union () } {}; + qualified_root (nano::uint512_t const & value) : + uint512_union{ value } {}; nano::root root () const { @@ -501,6 +477,7 @@ bool from_string_hex (std::string const &, uint64_t &); std::ostream & operator<< (std::ostream &, const uint128_union &); std::ostream & operator<< (std::ostream &, const uint256_union &); std::ostream & operator<< (std::ostream &, const uint512_union &); +std::ostream & operator<< (std::ostream &, const hash_or_account &); /** * Convert a double to string in fixed format @@ -643,6 +620,11 @@ struct fmt::formatter : fmt::ostream_formatter { }; +template <> +struct fmt::formatter : fmt::ostream_formatter +{ +}; + template <> struct fmt::formatter : fmt::formatter { @@ -656,4 +638,4 @@ struct fmt::formatter : fmt::formatter template <> struct fmt::formatter : fmt::formatter { -}; \ No newline at end of file +}; diff --git a/nano/node/bootstrap/bootstrap.cpp b/nano/node/bootstrap/bootstrap.cpp index 3db711dfce..8779400eec 100644 --- a/nano/node/bootstrap/bootstrap.cpp +++ b/nano/node/bootstrap/bootstrap.cpp @@ -313,7 +313,7 @@ void nano::pulls_cache::add (nano::pull_info const & pull_a) cache.erase (cache.begin ()); } debug_assert (cache.size () <= cache_size_max); - nano::uint512_union head_512 (pull_a.account_or_head, pull_a.head_original); + nano::uint512_union head_512 (pull_a.account_or_head.as_union (), pull_a.head_original); auto existing (cache.get ().find (head_512)); if (existing == cache.get ().end ()) { @@ -336,7 +336,7 @@ void nano::pulls_cache::add (nano::pull_info const & pull_a) void nano::pulls_cache::update_pull (nano::pull_info & pull_a) { nano::lock_guard guard{ pulls_cache_mutex }; - nano::uint512_union head_512 (pull_a.account_or_head, pull_a.head_original); + nano::uint512_union head_512 (pull_a.account_or_head.as_union (), pull_a.head_original); auto existing (cache.get ().find (head_512)); if (existing != cache.get ().end ()) { @@ -347,7 +347,7 @@ void nano::pulls_cache::update_pull (nano::pull_info & pull_a) void nano::pulls_cache::remove (nano::pull_info const & pull_a) { nano::lock_guard guard{ pulls_cache_mutex }; - nano::uint512_union head_512 (pull_a.account_or_head, pull_a.head_original); + nano::uint512_union head_512 (pull_a.account_or_head.as_union (), pull_a.head_original); cache.get ().erase (head_512); } From 2b2fd12106b8be409362dc84d28ea9a3db3ab389 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Fri, 25 Oct 2024 12:41:45 +0200 Subject: [PATCH 8/9] Hashing cleanup --- nano/core_test/numbers.cpp | 94 ++++++++++++++++++++ nano/lib/numbers.hpp | 160 ++++++++++++++++++++++++++++------- nano/secure/common.hpp | 55 ------------ nano/secure/pending_info.hpp | 4 +- 4 files changed, 224 insertions(+), 89 deletions(-) diff --git a/nano/core_test/numbers.cpp b/nano/core_test/numbers.cpp index 1ffe78808b..d6b141fa58 100644 --- a/nano/core_test/numbers.cpp +++ b/nano/core_test/numbers.cpp @@ -3,7 +3,10 @@ #include +#include + #include +#include namespace { @@ -577,3 +580,94 @@ void check_operator_greater_than (Num lhs, Num rhs) ASSERT_FALSE (rhs > rhs); } } + +namespace +{ +template class Hash> +void test_hashing () +{ + Hash hash; + using underlying_t = typename Type::underlying_type; + + // Basic equality tests + ASSERT_EQ (hash (Type{}), hash (Type{})); + ASSERT_EQ (hash (Type{ 123 }), hash (Type{ 123 })); + + // Basic inequality tests + ASSERT_NE (hash (Type{ 123 }), hash (Type{ 124 })); + ASSERT_NE (hash (Type{ 0 }), hash (Type{ 1 })); + + // Boundary value tests + constexpr auto min_val = std::numeric_limits::min (); + constexpr auto max_val = std::numeric_limits::max (); + + // Min/Max tests + ASSERT_EQ (hash (Type{ min_val }), hash (Type{ min_val })); + ASSERT_EQ (hash (Type{ max_val }), hash (Type{ max_val })); + ASSERT_NE (hash (Type{ min_val }), hash (Type{ max_val })); + + // Near boundary tests + ASSERT_NE (hash (Type{ min_val }), hash (Type{ min_val + 1 })); + ASSERT_NE (hash (Type{ max_val }), hash (Type{ max_val - 1 })); + ASSERT_NE (hash (Type{ min_val + 1 }), hash (Type{ max_val })); + ASSERT_NE (hash (Type{ max_val - 1 }), hash (Type{ min_val })); + + // Common value tests + std::vector common_values = { + 0, // Zero + 1, // One + 42, // Common test value + 0xFF, // Byte boundary + 0xFFFF, // Word boundary + min_val, // Minimum + max_val, // Maximum + max_val / 2, // Middle value + min_val + (max_val / 2) // Offset middle + }; + + // Test all common values against each other + for (size_t i = 0; i < common_values.size (); ++i) + { + for (size_t j = i + 1; j < common_values.size (); ++j) + { + if (common_values[i] != common_values[j]) + { + ASSERT_NE (hash (Type{ common_values[i] }), hash (Type{ common_values[j] })); + } + else + { + ASSERT_EQ (hash (Type{ common_values[i] }), hash (Type{ common_values[j] })); + } + } + } +} +} + +TEST (numbers, hashing) +{ + // Using std::hash + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + + // Using boost::hash + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); +} diff --git a/nano/lib/numbers.hpp b/nano/lib/numbers.hpp index 88e4de560d..bf8137c657 100644 --- a/nano/lib/numbers.hpp +++ b/nano/lib/numbers.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -22,6 +23,10 @@ nano::uint128_t const raw_ratio = nano::uint128_t ("1"); // 10^0 class uint128_union { +public: + // Type that is implicitly convertible to this union + using underlying_type = nano::uint128_t; + public: uint128_union () = default; uint128_union (uint64_t value) : @@ -115,11 +120,15 @@ class raw_key; class uint256_union { +public: + // Type that is implicitly convertible to this union + using underlying_type = nano::uint256_t; + public: uint256_union () = default; uint256_union (uint64_t value) : uint256_union (nano::uint256_t{ value }){}; - uint256_union (uint256_t const & value) + uint256_union (nano::uint256_t const & value) { bytes.fill (0); boost::multiprecision::export_bits (value, bytes.rbegin (), 8, false); @@ -256,6 +265,10 @@ using account = public_key; class hash_or_account { +public: + // Type that is implicitly convertible to this union + using underlying_type = nano::uint256_t; + public: hash_or_account () : account{} {}; @@ -369,6 +382,10 @@ class raw_key final : public uint256_union class uint512_union { +public: + // Type that is implicitly convertible to this union + using underlying_type = nano::uint512_t; + public: uint512_union () = default; uint512_union (nano::uint512_t const & value) @@ -499,83 +516,91 @@ namespace difficulty namespace std { template <> +struct hash<::nano::uint128_union> +{ + size_t operator() (::nano::uint128_union const & value) const noexcept + { + return value.qwords[0] + value.qwords[1]; + } +}; +template <> struct hash<::nano::uint256_union> { - size_t operator() (::nano::uint256_union const & data_a) const + size_t operator() (::nano::uint256_union const & value) const noexcept { - return data_a.qwords[0] + data_a.qwords[1] + data_a.qwords[2] + data_a.qwords[3]; + return value.qwords[0] + value.qwords[1] + value.qwords[2] + value.qwords[3]; } }; template <> -struct hash<::nano::account> +struct hash<::nano::public_key> { - size_t operator() (::nano::account const & data_a) const + size_t operator() (::nano::public_key const & value) const noexcept { - return hash<::nano::uint256_union> () (data_a); + return hash<::nano::uint256_union>{}(value); } }; template <> struct hash<::nano::block_hash> { - size_t operator() (::nano::block_hash const & data_a) const + size_t operator() (::nano::block_hash const & value) const noexcept { - return hash<::nano::uint256_union> () (data_a); + return hash<::nano::uint256_union>{}(value); } }; template <> struct hash<::nano::hash_or_account> { - size_t operator() (::nano::hash_or_account const & data_a) const + size_t operator() (::nano::hash_or_account const & value) const noexcept { - return hash<::nano::block_hash> () (data_a.as_block_hash ()); + return hash<::nano::block_hash>{}(value.as_block_hash ()); } }; template <> -struct hash<::nano::raw_key> +struct hash<::nano::root> { - size_t operator() (::nano::raw_key const & data_a) const + size_t operator() (::nano::root const & value) const noexcept { - return hash<::nano::uint256_union> () (data_a); + return hash<::nano::hash_or_account>{}(value); } }; template <> -struct hash<::nano::root> +struct hash<::nano::link> { - size_t operator() (::nano::root const & data_a) const + size_t operator() (::nano::link const & value) const noexcept { - return hash<::nano::uint256_union> () (data_a); + return hash<::nano::hash_or_account>{}(value); } }; template <> -struct hash<::nano::wallet_id> +struct hash<::nano::raw_key> { - size_t operator() (::nano::wallet_id const & data_a) const + size_t operator() (::nano::raw_key const & value) const noexcept { - return hash<::nano::uint256_union> () (data_a); + return hash<::nano::uint256_union>{}(value); } }; template <> -struct hash<::nano::uint256_t> +struct hash<::nano::wallet_id> { - size_t operator() (::nano::uint256_t const & number_a) const + size_t operator() (::nano::wallet_id const & value) const noexcept { - return number_a.convert_to (); + return hash<::nano::uint256_union>{}(value); } }; template <> struct hash<::nano::uint512_union> { - size_t operator() (::nano::uint512_union const & data_a) const + size_t operator() (::nano::uint512_union const & value) const noexcept { - return hash<::nano::uint256_union> () (data_a.uint256s[0]) + hash<::nano::uint256_union> () (data_a.uint256s[1]); + return hash<::nano::uint256_union>{}(value.uint256s[0]) + hash<::nano::uint256_union> () (value.uint256s[1]); } }; template <> struct hash<::nano::qualified_root> { - size_t operator() (::nano::qualified_root const & data_a) const + size_t operator() (::nano::qualified_root const & value) const noexcept { - return hash<::nano::uint512_union> () (data_a); + return hash<::nano::uint512_union>{}(value); } }; } @@ -583,20 +608,91 @@ struct hash<::nano::qualified_root> namespace boost { template <> -struct hash> +struct hash<::nano::uint128_union> +{ + size_t operator() (::nano::uint128_union const & value) const noexcept + { + return std::hash<::nano::uint128_union> () (value); + } +}; +template <> +struct hash<::nano::uint256_union> +{ + size_t operator() (::nano::uint256_union const & value) const noexcept + { + return std::hash<::nano::uint256_union> () (value); + } +}; +template <> +struct hash<::nano::public_key> +{ + size_t operator() (::nano::public_key const & value) const noexcept + { + return std::hash<::nano::public_key> () (value); + } +}; +template <> +struct hash<::nano::block_hash> { - size_t operator() (std::reference_wrapper<::nano::block_hash const> const & hash_a) const + size_t operator() (::nano::block_hash const & value) const noexcept { - std::hash<::nano::block_hash> hash; - return hash (hash_a); + return std::hash<::nano::block_hash> () (value); + } +}; +template <> +struct hash<::nano::hash_or_account> +{ + size_t operator() (::nano::hash_or_account const & value) const noexcept + { + return std::hash<::nano::hash_or_account> () (value); } }; template <> struct hash<::nano::root> { - size_t operator() (::nano::root const & value_a) const + size_t operator() (::nano::root const & value) const noexcept + { + return std::hash<::nano::root> () (value); + } +}; +template <> +struct hash<::nano::link> +{ + size_t operator() (::nano::link const & value) const noexcept + { + return std::hash<::nano::link> () (value); + } +}; +template <> +struct hash<::nano::raw_key> +{ + size_t operator() (::nano::raw_key const & value) const noexcept + { + return std::hash<::nano::raw_key> () (value); + } +}; +template <> +struct hash<::nano::wallet_id> +{ + size_t operator() (::nano::wallet_id const & value) const noexcept + { + return std::hash<::nano::wallet_id> () (value); + } +}; +template <> +struct hash<::nano::uint512_union> +{ + size_t operator() (::nano::uint512_union const & value) const noexcept + { + return std::hash<::nano::uint512_union> () (value); + } +}; +template <> +struct hash<::nano::qualified_root> +{ + size_t operator() (::nano::qualified_root const & value) const noexcept { - return std::hash<::nano::root> () (value_a); + return std::hash<::nano::qualified_root> () (value); } }; } diff --git a/nano/secure/common.hpp b/nano/secure/common.hpp index fd28d30b13..e1639bd265 100644 --- a/nano/secure/common.hpp +++ b/nano/secure/common.hpp @@ -21,61 +21,6 @@ #include #include -namespace boost -{ -template <> -struct hash<::nano::uint256_union> -{ - size_t operator() (::nano::uint256_union const & value_a) const - { - return std::hash<::nano::uint256_union> () (value_a); - } -}; - -template <> -struct hash<::nano::block_hash> -{ - size_t operator() (::nano::block_hash const & value_a) const - { - return std::hash<::nano::block_hash> () (value_a); - } -}; - -template <> -struct hash<::nano::hash_or_account> -{ - size_t operator() (::nano::hash_or_account const & data_a) const - { - return std::hash<::nano::hash_or_account> () (data_a); - } -}; - -template <> -struct hash<::nano::public_key> -{ - size_t operator() (::nano::public_key const & value_a) const - { - return std::hash<::nano::public_key> () (value_a); - } -}; -template <> -struct hash<::nano::uint512_union> -{ - size_t operator() (::nano::uint512_union const & value_a) const - { - return std::hash<::nano::uint512_union> () (value_a); - } -}; -template <> -struct hash<::nano::qualified_root> -{ - size_t operator() (::nano::qualified_root const & value_a) const - { - return std::hash<::nano::qualified_root> () (value_a); - } -}; -} - namespace nano { /** diff --git a/nano/secure/pending_info.hpp b/nano/secure/pending_info.hpp index 2d57df9ba2..c00e1cdb39 100644 --- a/nano/secure/pending_info.hpp +++ b/nano/secure/pending_info.hpp @@ -67,9 +67,9 @@ namespace std template <> struct hash<::nano::pending_key> { - size_t operator() (::nano::pending_key const & data_a) const + size_t operator() (::nano::pending_key const & value) const { - return hash<::nano::uint512_union>{}({ ::nano::uint256_union{ data_a.account.number () }, data_a.hash }); + return hash<::nano::uint512_union>{}({ ::nano::uint256_union{ value.account.number () }, value.hash }); } }; } From 5d341f44b88425ca782b00f87a7acabe3ed2b29e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Fri, 25 Oct 2024 20:16:54 +0200 Subject: [PATCH 9/9] Test comparison for all types --- nano/core_test/numbers.cpp | 371 +++++++++++++++++-------------------- 1 file changed, 174 insertions(+), 197 deletions(-) diff --git a/nano/core_test/numbers.cpp b/nano/core_test/numbers.cpp index d6b141fa58..a30b2eb108 100644 --- a/nano/core_test/numbers.cpp +++ b/nano/core_test/numbers.cpp @@ -8,22 +8,185 @@ #include #include +TEST (numbers, identity) +{ + ASSERT_EQ (1, nano::uint128_union (1).number ().convert_to ()); + ASSERT_EQ (1, nano::uint256_union (1).number ().convert_to ()); + ASSERT_EQ (1, nano::uint512_union (1).number ().convert_to ()); +} + +namespace +{ +template +void check_operator_less_than (Type lhs, Type rhs) +{ + ASSERT_TRUE (lhs < rhs); + ASSERT_FALSE (rhs < lhs); + ASSERT_FALSE (lhs < lhs); + ASSERT_FALSE (rhs < rhs); +} + +template +void test_operator_less_than () +{ + using underlying_t = typename Type::underlying_type; + + // Small + check_operator_less_than (Type{ 123 }, Type{ 124 }); + check_operator_less_than (Type{ 124 }, Type{ 125 }); + + // Medium + check_operator_less_than (Type{ std::numeric_limits::max () - 1 }, Type{ std::numeric_limits::max () + 1 }); + check_operator_less_than (Type{ std::numeric_limits::max () - 12345678 }, Type{ std::numeric_limits::max () - 123456 }); + + // Large + check_operator_less_than (Type{ std::numeric_limits::max () - 555555555555 }, Type{ std::numeric_limits::max () - 1 }); + + // Boundary values + check_operator_less_than (Type{ std::numeric_limits::min () }, Type{ std::numeric_limits::max () }); +} + +template +void check_operator_greater_than (Type lhs, Type rhs) +{ + ASSERT_TRUE (lhs > rhs); + ASSERT_FALSE (rhs > lhs); + ASSERT_FALSE (lhs > lhs); + ASSERT_FALSE (rhs > rhs); +} + +template +void test_operator_greater_than () +{ + using underlying_t = typename Type::underlying_type; + + // Small + check_operator_greater_than (Type{ 124 }, Type{ 123 }); + check_operator_greater_than (Type{ 125 }, Type{ 124 }); + + // Medium + check_operator_greater_than (Type{ std::numeric_limits::max () + 1 }, Type{ std::numeric_limits::max () - 1 }); + check_operator_greater_than (Type{ std::numeric_limits::max () - 123456 }, Type{ std::numeric_limits::max () - 12345678 }); + + // Large + check_operator_greater_than (Type{ std::numeric_limits::max () - 1 }, Type{ std::numeric_limits::max () - 555555555555 }); + + // Boundary values + check_operator_greater_than (Type{ std::numeric_limits::max () }, Type{ std::numeric_limits::min () }); +} + +template +void test_comparison () +{ + test_operator_less_than (); + test_operator_greater_than (); +} +} + +TEST (numbers, comparison) +{ + test_comparison (); + test_comparison (); + test_comparison (); + test_comparison (); + test_comparison (); + test_comparison (); + test_comparison (); + test_comparison (); + test_comparison (); + test_comparison (); + test_comparison (); +} + namespace { -template -void assert_union_types (); +template class Hash> +void test_hashing () +{ + Hash hash; + using underlying_t = typename Type::underlying_type; + + // Basic equality tests + ASSERT_EQ (hash (Type{}), hash (Type{})); + ASSERT_EQ (hash (Type{ 123 }), hash (Type{ 123 })); + + // Basic inequality tests + ASSERT_NE (hash (Type{ 123 }), hash (Type{ 124 })); + ASSERT_NE (hash (Type{ 0 }), hash (Type{ 1 })); + + // Boundary value tests + constexpr auto min_val = std::numeric_limits::min (); + constexpr auto max_val = std::numeric_limits::max (); + + // Min/Max tests + ASSERT_EQ (hash (Type{ min_val }), hash (Type{ min_val })); + ASSERT_EQ (hash (Type{ max_val }), hash (Type{ max_val })); + ASSERT_NE (hash (Type{ min_val }), hash (Type{ max_val })); + + // Near boundary tests + ASSERT_NE (hash (Type{ min_val }), hash (Type{ min_val + 1 })); + ASSERT_NE (hash (Type{ max_val }), hash (Type{ max_val - 1 })); + ASSERT_NE (hash (Type{ min_val + 1 }), hash (Type{ max_val })); + ASSERT_NE (hash (Type{ max_val - 1 }), hash (Type{ min_val })); -template -void test_union_operator_less_than (); + // Common value tests + std::vector common_values = { + 0, // Zero + 1, // One + 42, // Common test value + 0xFF, // Byte boundary + 0xFFFF, // Word boundary + min_val, // Minimum + max_val, // Maximum + max_val / 2, // Middle value + min_val + (max_val / 2) // Offset middle + }; -template -void check_operator_less_than (Num lhs, Num rhs); + // Test all common values against each other + for (size_t i = 0; i < common_values.size (); ++i) + { + for (size_t j = i + 1; j < common_values.size (); ++j) + { + if (common_values[i] != common_values[j]) + { + ASSERT_NE (hash (Type{ common_values[i] }), hash (Type{ common_values[j] })); + } + else + { + ASSERT_EQ (hash (Type{ common_values[i] }), hash (Type{ common_values[j] })); + } + } + } +} +} -template -void test_union_operator_greater_than (); +TEST (numbers, hashing) +{ + // Using std::hash + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); -template -void check_operator_greater_than (Num lhs, Num rhs); + // Using boost::hash + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); + test_hashing (); } TEST (uint128_union, decode_dec) @@ -66,16 +229,6 @@ TEST (uint128_union, decode_dec_overflow) ASSERT_TRUE (error); } -TEST (uint128_union, operator_less_than) -{ - test_union_operator_less_than (); -} - -TEST (uint128_union, operator_greater_than) -{ - test_union_operator_greater_than (); -} - struct test_punct : std::moneypunct { pattern do_pos_format () const @@ -151,13 +304,6 @@ TEST (uint128_union, decode_decimal) ASSERT_EQ (1230 * nano::Knano_ratio, amount.number ()); } -TEST (unions, identity) -{ - ASSERT_EQ (1, nano::uint128_union (1).number ().convert_to ()); - ASSERT_EQ (1, nano::uint256_union (1).number ().convert_to ()); - ASSERT_EQ (1, nano::uint512_union (1).number ().convert_to ()); -} - TEST (uint256_union, key_encryption) { nano::keypair key1; @@ -451,16 +597,6 @@ TEST (uint256_union, bounds) ASSERT_TRUE (key.decode_account (bad2)); } -TEST (uint256_union, operator_less_than) -{ - test_union_operator_less_than (); -} - -TEST (uint256_union, operator_greater_than) -{ - test_union_operator_greater_than (); -} - TEST (uint64_t, parse) { uint64_t value0 (1); @@ -511,163 +647,4 @@ TEST (uint512_union, hash) ASSERT_NE (h (x1), h (x2)); } } -} - -namespace -{ -template -void assert_union_types () -{ - static_assert ((std::is_same::value && std::is_same::value) || (std::is_same::value && std::is_same::value) || (std::is_same::value && std::is_same::value), - "Union type needs to be consistent with the lower/upper Bound type"); -} - -template -void test_union_operator_less_than () -{ - assert_union_types (); - - // Small - check_operator_less_than (Union (123), Union (124)); - check_operator_less_than (Union (124), Union (125)); - - // Medium - check_operator_less_than (Union (std::numeric_limits::max () - 1), Union (std::numeric_limits::max () + 1)); - check_operator_less_than (Union (std::numeric_limits::max () - 12345678), Union (std::numeric_limits::max () - 123456)); - - // Large - check_operator_less_than (Union (std::numeric_limits::max () - 555555555555), Union (std::numeric_limits::max () - 1)); - - // Boundary values - check_operator_less_than (Union (std::numeric_limits::min ()), Union (std::numeric_limits::max ())); -} - -template -void check_operator_less_than (Num lhs, Num rhs) -{ - ASSERT_TRUE (lhs < rhs); - ASSERT_FALSE (rhs < lhs); - ASSERT_FALSE (lhs < lhs); - ASSERT_FALSE (rhs < rhs); -} - -template -void test_union_operator_greater_than () -{ - assert_union_types (); - - // Small - check_operator_greater_than (Union (124), Union (123)); - check_operator_greater_than (Union (125), Union (124)); - - // Medium - check_operator_greater_than (Union (std::numeric_limits::max () + 1), Union (std::numeric_limits::max () - 1)); - check_operator_greater_than (Union (std::numeric_limits::max () - 123456), Union (std::numeric_limits::max () - 12345678)); - - // Large - check_operator_greater_than (Union (std::numeric_limits::max () - 1), Union (std::numeric_limits::max () - 555555555555)); - - // Boundary values - check_operator_greater_than (Union (std::numeric_limits::max ()), Union (std::numeric_limits::min ())); -} - -template -void check_operator_greater_than (Num lhs, Num rhs) -{ - ASSERT_TRUE (lhs > rhs); - ASSERT_FALSE (rhs > lhs); - ASSERT_FALSE (lhs > lhs); - ASSERT_FALSE (rhs > rhs); -} -} - -namespace -{ -template class Hash> -void test_hashing () -{ - Hash hash; - using underlying_t = typename Type::underlying_type; - - // Basic equality tests - ASSERT_EQ (hash (Type{}), hash (Type{})); - ASSERT_EQ (hash (Type{ 123 }), hash (Type{ 123 })); - - // Basic inequality tests - ASSERT_NE (hash (Type{ 123 }), hash (Type{ 124 })); - ASSERT_NE (hash (Type{ 0 }), hash (Type{ 1 })); - - // Boundary value tests - constexpr auto min_val = std::numeric_limits::min (); - constexpr auto max_val = std::numeric_limits::max (); - - // Min/Max tests - ASSERT_EQ (hash (Type{ min_val }), hash (Type{ min_val })); - ASSERT_EQ (hash (Type{ max_val }), hash (Type{ max_val })); - ASSERT_NE (hash (Type{ min_val }), hash (Type{ max_val })); - - // Near boundary tests - ASSERT_NE (hash (Type{ min_val }), hash (Type{ min_val + 1 })); - ASSERT_NE (hash (Type{ max_val }), hash (Type{ max_val - 1 })); - ASSERT_NE (hash (Type{ min_val + 1 }), hash (Type{ max_val })); - ASSERT_NE (hash (Type{ max_val - 1 }), hash (Type{ min_val })); - - // Common value tests - std::vector common_values = { - 0, // Zero - 1, // One - 42, // Common test value - 0xFF, // Byte boundary - 0xFFFF, // Word boundary - min_val, // Minimum - max_val, // Maximum - max_val / 2, // Middle value - min_val + (max_val / 2) // Offset middle - }; - - // Test all common values against each other - for (size_t i = 0; i < common_values.size (); ++i) - { - for (size_t j = i + 1; j < common_values.size (); ++j) - { - if (common_values[i] != common_values[j]) - { - ASSERT_NE (hash (Type{ common_values[i] }), hash (Type{ common_values[j] })); - } - else - { - ASSERT_EQ (hash (Type{ common_values[i] }), hash (Type{ common_values[j] })); - } - } - } -} -} - -TEST (numbers, hashing) -{ - // Using std::hash - test_hashing (); - test_hashing (); - test_hashing (); - test_hashing (); - test_hashing (); - test_hashing (); - test_hashing (); - test_hashing (); - test_hashing (); - test_hashing (); - test_hashing (); - - // Using boost::hash - test_hashing (); - test_hashing (); - test_hashing (); - test_hashing (); - test_hashing (); - test_hashing (); - test_hashing (); - test_hashing (); - test_hashing (); - test_hashing (); - test_hashing (); -} +} \ No newline at end of file