-
Notifications
You must be signed in to change notification settings - Fork 37.7k
refactor: modernize the implementation of uint256.* #26345
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor: modernize the implementation of uint256.* #26345
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Concept ACK
Win64 CI failure seems unrelated, would be nice if that could be restarted, or otherwise disregard the failure. |
I believe you can restart the job yourself on the cirrus-ci's job page if you have a cirrus-ci account. |
Concept ACK |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Win64 CI failure seems unrelated, would be nice if that could be restarted, or otherwise disregard the failure.
The failure is actually related, not sure why, but can be fixed with this patch:
Fix
diff --git a/src/uint256.h b/src/uint256.h
index d23b2b39a..d5d318baf 100644
--- a/src/uint256.h
+++ b/src/uint256.h
@@ -63,12 +63,11 @@ public:
constexpr const unsigned char* data() const { return m_data.data(); }
constexpr unsigned char* data() { return m_data.data(); }
- constexpr unsigned char* begin() { return m_data.begin();}
- constexpr unsigned char* end() { return m_data.end(); }
+ constexpr unsigned char* begin() { return m_data.data(); }
+ constexpr unsigned char* end() { return m_data.data() + WIDTH; }
- constexpr const unsigned char* begin() const { return m_data.begin(); }
-
- constexpr const unsigned char* end() const { return m_data.end(); }
+ constexpr const unsigned char* begin() const { return m_data.data(); }
+ constexpr const unsigned char* end() const { return m_data.data() + WIDTH; }
static constexpr unsigned int size() { return WIDTH; }
See https://cirrus-ci.com/build/5052567852417024 for proof.
I've pushed 3 additional commits, removing inline where constexpr exists, and then I pushed a commit that adjusts begin() and end() as you suggested, although I think a better solution there may be to simply return auto or to transform the code to be okay with an iterator, but that seems to cause other issues (so I've reverted it). Is there any way I can "Win64 native" task on macOS m1 and try to figure that out better? (Note: I understand all commits will want to be squashed before merge, just trying to figure out those begin() end() methods a bit better atm) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
6e82a07
to
269481a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK 269481a
Any movement here? I know review time is precious, but I'd love to see this moved forward 🙈 |
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK 269481a, one nit
src/psbt.h
Outdated
@@ -889,7 +889,7 @@ struct PSBTOutput | |||
} else if (key.size() != 33) { | |||
throw std::ios_base::failure("Output Taproot BIP32 keypath key is not at 33 bytes"); | |||
} | |||
XOnlyPubKey xonly(uint256({key.begin() + 1, key.begin() + 33})); | |||
XOnlyPubKey xonly(uint256(Span<uint8_t>(key).subspan(/*offset=*/1, /*count=*/32))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Simpler:
XOnlyPubKey xonly(Span<uint8_t>{key}.last(32));
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With such a nice modernization refactoring it seems reasonable to add uint256.cpp
to the list of files being processed by the IWYU tool in the CI:
--- a/ci/test/06_script_b.sh
+++ b/ci/test/06_script_b.sh
@@ -57,6 +57,7 @@ if [ "${RUN_TIDY}" = "true" ]; then
" src/rpc/signmessage.cpp"\
" src/test/fuzz/txorphan.cpp"\
" src/test/fuzz/util/"\
+ " src/uint256.cpp"\
" src/util/bip32.cpp"\
" src/util/bytevectorhash.cpp"\
" src/util/check.cpp"\
and adjust #include
s as suggested by the tool.
src/uint256.h
Outdated
public: | ||
/* construct 0 value by default */ | ||
constexpr base_blob() : m_data() {} | ||
|
||
/* constructor for constants between 1 and 255 */ | ||
constexpr explicit base_blob(uint8_t v) : m_data{v} {} | ||
|
||
explicit base_blob(const std::vector<unsigned char>& vch); | ||
constexpr base_blob(Span<const unsigned char> vch) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this constructor is not explicit
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point.. Still compiles with it, so I may have missed this one :)
I've yet to figure out running this tool / ci locally on my m1 machine.. If you'd like to provide a diff of the includes I can apply it. |
Here you are: diff --git a/ci/test/06_script_b.sh b/ci/test/06_script_b.sh
index 09ce0a1ff1..b1742a5958 100755
--- a/ci/test/06_script_b.sh
+++ b/ci/test/06_script_b.sh
@@ -55,6 +55,7 @@ if [ "${RUN_TIDY}" = "true" ]; then
" src/rpc/signmessage.cpp"\
" src/test/fuzz/txorphan.cpp"\
" src/test/fuzz/util/"\
+ " src/uint256.cpp"\
" src/util/bip32.cpp"\
" src/util/bytevectorhash.cpp"\
" src/util/check.cpp"\
diff --git a/src/uint256.cpp b/src/uint256.cpp
index c31e81003e..7f81c3c448 100644
--- a/src/uint256.cpp
+++ b/src/uint256.cpp
@@ -7,8 +7,6 @@
#include <util/strencodings.h>
-#include <string.h>
-
template <unsigned int BITS>
std::string base_blob<BITS>::GetHex() const
{
diff --git a/src/uint256.h b/src/uint256.h
index 240b9eb4c4..782fec8e51 100644
--- a/src/uint256.h
+++ b/src/uint256.h
@@ -9,12 +9,12 @@
#include <crypto/common.h>
#include <span.h>
+#include <algorithm>
#include <array>
#include <cassert>
#include <cstring>
#include <stdint.h>
#include <string>
-#include <vector>
/** Template base class for fixed-sized opaque blobs. */
template<unsigned int BITS> And squash your correction commits, please. |
2dd9f41
to
dd7d6d1
Compare
Applied and squashed (and then rebased on master, as there was now a conflict) seeking re-ACKs @aureleoules @sipa |
dd7d6d1
to
9d88a7f
Compare
May I remind about #26345 (comment)? |
9d88a7f
to
5ec99b7
Compare
Sure, why not. Done and fped |
It looks diff --git a/src/consensus/params.h b/src/consensus/params.h
index 7c35222713..3b5eb1034d 100644
--- a/src/consensus/params.h
+++ b/src/consensus/params.h
@@ -11,6 +11,7 @@
#include <chrono>
#include <limits>
#include <map>
+#include <vector>
namespace Consensus {
diff --git a/src/random.h b/src/random.h
index 5fe20c5f76..082ccd4047 100644
--- a/src/random.h
+++ b/src/random.h
@@ -15,6 +15,7 @@
#include <chrono>
#include <cstdint>
#include <limits>
+#include <vector>
/**
* Overall design of the RNG and entropy sources. |
- Constructors of uint256 to utilize Span instead of requiring a std::vector - converts m_data into a std::array - Prefers using `WIDTH` instead of `sizeof(m_data)` - make all the things constexpr - replace C style functions with c++ equivalents - memset -> std::fill - memcpy -> std::copy Note: In practice, implementations of std::copy avoid multiple assignments and use bulk copy functions such as std::memmove if the value type is TriviallyCopyable and the iterator types satisfy LegacyContiguousIterator. (https://en.cppreference.com/w/cpp/algorithm/copy) - memcmp -> std::memcmp
5ec99b7
to
935acdc
Compare
Whoops 🙈 that's what happens when I forget to compile before pushing. Applied change, compiled, and force pushed :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approach ACK 935acdc.
I'm curios which part of C++ standard guarantees that the following comment is correct: Lines 29 to 30 in 935acdc
Will it result in indeterminate values of |
I'm not sure how exactly it's guaranteed, but I'm pretty sure it is. I would point towards https://en.cppreference.com/w/cpp/language/zero_initialization Additionally, you're able to do this at compile time, and undefined behavior is prohibited at compile time by standard, so that makes me think it's well defined at runtime as well.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reACK 935acdc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK 935acdc
Did a couple quick and dirty sanity-check benchmarks and everything is the same or slightly faster (like Span
constructor, as expected).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approach ACK 935acdc
Not a very high confidence review, I'm not very familiar with the nuances of switching from C to C++ functions/objects - may be missing unintentional side effects.
|
||
/** Template base class for fixed-sized opaque blobs. */ | ||
template<unsigned int BITS> | ||
class base_blob | ||
{ | ||
protected: | ||
static constexpr int WIDTH = BITS / 8; | ||
uint8_t m_data[WIDTH]; | ||
std::array<uint8_t, WIDTH> m_data; | ||
static_assert(WIDTH == sizeof(m_data), "Sanity check"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that we've just defined m_data
to be of size WIDTH
the line above, this check seems unnecessary bloat?
"Sanity check" isn't adding any value as a message imo.
if (m_data[i] != 0) | ||
return false; | ||
return true; | ||
return std::all_of(m_data.begin(), m_data.end(), [](uint8_t val) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would avoid redefining the value type of m_data
return std::all_of(m_data.begin(), m_data.end(), [](uint8_t val) { | |
return std::all_of(m_data.begin(), m_data.end(), [](auto val) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or alternatively, could also use typedef uint8_t value_t
?
git diff
diff --git a/src/uint256.h b/src/uint256.h
index 782fec8e5..0f657a656 100644
--- a/src/uint256.h
+++ b/src/uint256.h
@@ -21,8 +21,9 @@ template<unsigned int BITS>
class base_blob
{
protected:
+ typedef uint8_t value_t;
static constexpr int WIDTH = BITS / 8;
- std::array<uint8_t, WIDTH> m_data;
+ std::array<value_t, WIDTH> m_data;
static_assert(WIDTH == sizeof(m_data), "Sanity check");
public:
@@ -30,7 +31,7 @@ public:
constexpr base_blob() : m_data() {}
/* constructor for constants between 1 and 255 */
- constexpr explicit base_blob(uint8_t v) : m_data{v} {}
+ constexpr explicit base_blob(value_t v) : m_data{v} {}
constexpr explicit base_blob(Span<const unsigned char> vch)
{
@@ -40,7 +41,7 @@ public:
constexpr bool IsNull() const
{
- return std::all_of(m_data.begin(), m_data.end(), [](uint8_t val) {
+ return std::all_of(m_data.begin(), m_data.end(), [](value_t val) {
return val == 0;
});
}
@@ -105,7 +106,7 @@ public:
class uint256 : public base_blob<256> {
public:
constexpr uint256() = default;
- constexpr explicit uint256(uint8_t v) : base_blob<256>(v) {}
+ constexpr explicit uint256(value_t v) : base_blob<256>(v) {}
constexpr explicit uint256(Span<const unsigned char> vch) : base_blob<256>(vch) {}
static const uint256 ZERO;
static const uint256 ONE;
} | ||
|
||
inline int Compare(const base_blob& other) const { return memcmp(m_data, other.m_data, sizeof(m_data)); } | ||
constexpr int Compare(const base_blob& other) const { return std::memcmp(m_data.data(), other.m_data.data(), WIDTH); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: we've already defined member function data()
to return m_data.data()
(+ in a bunch more lines further down and in uint256.cpp
)
constexpr int Compare(const base_blob& other) const { return std::memcmp(m_data.data(), other.m_data.data(), WIDTH); } | |
constexpr int Compare(const base_blob& other) const { return std::memcmp(data(), other.data(), WIDTH); } |
@@ -889,7 +889,7 @@ struct PSBTOutput | |||
} else if (key.size() != 33) { | |||
throw std::ios_base::failure("Output Taproot BIP32 keypath key is not at 33 bytes"); | |||
} | |||
XOnlyPubKey xonly(uint256({key.begin() + 1, key.begin() + 33})); | |||
XOnlyPubKey xonly(uint256(Span<uint8_t>(key).last(32))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit
XOnlyPubKey xonly(uint256(Span<uint8_t>(key).last(32))); | |
XOnlyPubKey xonly(uint256{Span<uint8_t>(key).last(32)}); |
ACK 935acdc |
935acdc refactor: modernize the implementation of uint256.* (pasta) Pull request description: - Constructors of uint256 to utilize Span instead of requiring a std::vector - converts m_data into a std::array - Prefers using `WIDTH` instead of `sizeof(m_data)` - make all the things constexpr - replace C style functions with c++ equivalents - memset -> std::fill This may also be replaced by std::memset, but I think that std::fill is more idiomatic of modern c++ and readable. - memcpy -> std::copy Note: In practice, implementations of std::copy avoid multiple assignments and use bulk copy functions such as std::memmove if the value type is TriviallyCopyable and the iterator types satisfy LegacyContiguousIterator. (https://en.cppreference.com/w/cpp/algorithm/copy) This could also likely be replaced by std::memcpy, but as said above, I believe the using std::copy is the more c++ way to do anything and is almost guaranteed to compile to the same asm - memcmp -> std::memcmp ACKs for top commit: achow101: ACK 935acdc hebasto: Approach ACK 935acdc. aureleoules: reACK 935acdc john-moffett: ACK 935acdc stickies-v: Approach ACK 935acdc Tree-SHA512: 4f1ba54ff2198eea0e505d41e73d552c84c60f6878d5c85a94a8ab57f39afc94ef8d79258e7afd01fa84ec2a99f4404bb877eecd671f65e1ee9273f3129fc650
935acdc refactor: modernize the implementation of uint256.* (pasta) Pull request description: - Constructors of uint256 to utilize Span instead of requiring a std::vector - converts m_data into a std::array - Prefers using `WIDTH` instead of `sizeof(m_data)` - make all the things constexpr - replace C style functions with c++ equivalents - memset -> std::fill This may also be replaced by std::memset, but I think that std::fill is more idiomatic of modern c++ and readable. - memcpy -> std::copy Note: In practice, implementations of std::copy avoid multiple assignments and use bulk copy functions such as std::memmove if the value type is TriviallyCopyable and the iterator types satisfy LegacyContiguousIterator. (https://en.cppreference.com/w/cpp/algorithm/copy) This could also likely be replaced by std::memcpy, but as said above, I believe the using std::copy is the more c++ way to do anything and is almost guaranteed to compile to the same asm - memcmp -> std::memcmp ACKs for top commit: achow101: ACK 935acdc hebasto: Approach ACK 935acdc. aureleoules: reACK 935acdc john-moffett: ACK 935acdc stickies-v: Approach ACK 935acdc Tree-SHA512: 4f1ba54ff2198eea0e505d41e73d552c84c60f6878d5c85a94a8ab57f39afc94ef8d79258e7afd01fa84ec2a99f4404bb877eecd671f65e1ee9273f3129fc650
935acdc refactor: modernize the implementation of uint256.* (pasta) Pull request description: - Constructors of uint256 to utilize Span instead of requiring a std::vector - converts m_data into a std::array - Prefers using `WIDTH` instead of `sizeof(m_data)` - make all the things constexpr - replace C style functions with c++ equivalents - memset -> std::fill This may also be replaced by std::memset, but I think that std::fill is more idiomatic of modern c++ and readable. - memcpy -> std::copy Note: In practice, implementations of std::copy avoid multiple assignments and use bulk copy functions such as std::memmove if the value type is TriviallyCopyable and the iterator types satisfy LegacyContiguousIterator. (https://en.cppreference.com/w/cpp/algorithm/copy) This could also likely be replaced by std::memcpy, but as said above, I believe the using std::copy is the more c++ way to do anything and is almost guaranteed to compile to the same asm - memcmp -> std::memcmp ACKs for top commit: achow101: ACK 935acdc hebasto: Approach ACK 935acdc. aureleoules: reACK 935acdc john-moffett: ACK 935acdc stickies-v: Approach ACK 935acdc Tree-SHA512: 4f1ba54ff2198eea0e505d41e73d552c84c60f6878d5c85a94a8ab57f39afc94ef8d79258e7afd01fa84ec2a99f4404bb877eecd671f65e1ee9273f3129fc650
935acdc refactor: modernize the implementation of uint256.* (pasta) Pull request description: - Constructors of uint256 to utilize Span instead of requiring a std::vector - converts m_data into a std::array - Prefers using `WIDTH` instead of `sizeof(m_data)` - make all the things constexpr - replace C style functions with c++ equivalents - memset -> std::fill This may also be replaced by std::memset, but I think that std::fill is more idiomatic of modern c++ and readable. - memcpy -> std::copy Note: In practice, implementations of std::copy avoid multiple assignments and use bulk copy functions such as std::memmove if the value type is TriviallyCopyable and the iterator types satisfy LegacyContiguousIterator. (https://en.cppreference.com/w/cpp/algorithm/copy) This could also likely be replaced by std::memcpy, but as said above, I believe the using std::copy is the more c++ way to do anything and is almost guaranteed to compile to the same asm - memcmp -> std::memcmp ACKs for top commit: achow101: ACK 935acdc hebasto: Approach ACK 935acdc. aureleoules: reACK 935acdc john-moffett: ACK 935acdc stickies-v: Approach ACK 935acdc Tree-SHA512: 4f1ba54ff2198eea0e505d41e73d552c84c60f6878d5c85a94a8ab57f39afc94ef8d79258e7afd01fa84ec2a99f4404bb877eecd671f65e1ee9273f3129fc650
935acdc refactor: modernize the implementation of uint256.* (pasta) Pull request description: - Constructors of uint256 to utilize Span instead of requiring a std::vector - converts m_data into a std::array - Prefers using `WIDTH` instead of `sizeof(m_data)` - make all the things constexpr - replace C style functions with c++ equivalents - memset -> std::fill This may also be replaced by std::memset, but I think that std::fill is more idiomatic of modern c++ and readable. - memcpy -> std::copy Note: In practice, implementations of std::copy avoid multiple assignments and use bulk copy functions such as std::memmove if the value type is TriviallyCopyable and the iterator types satisfy LegacyContiguousIterator. (https://en.cppreference.com/w/cpp/algorithm/copy) This could also likely be replaced by std::memcpy, but as said above, I believe the using std::copy is the more c++ way to do anything and is almost guaranteed to compile to the same asm - memcmp -> std::memcmp ACKs for top commit: achow101: ACK 935acdc hebasto: Approach ACK 935acdc. aureleoules: reACK 935acdc john-moffett: ACK 935acdc stickies-v: Approach ACK 935acdc Tree-SHA512: 4f1ba54ff2198eea0e505d41e73d552c84c60f6878d5c85a94a8ab57f39afc94ef8d79258e7afd01fa84ec2a99f4404bb877eecd671f65e1ee9273f3129fc650
935acdc refactor: modernize the implementation of uint256.* (pasta) Pull request description: - Constructors of uint256 to utilize Span instead of requiring a std::vector - converts m_data into a std::array - Prefers using `WIDTH` instead of `sizeof(m_data)` - make all the things constexpr - replace C style functions with c++ equivalents - memset -> std::fill This may also be replaced by std::memset, but I think that std::fill is more idiomatic of modern c++ and readable. - memcpy -> std::copy Note: In practice, implementations of std::copy avoid multiple assignments and use bulk copy functions such as std::memmove if the value type is TriviallyCopyable and the iterator types satisfy LegacyContiguousIterator. (https://en.cppreference.com/w/cpp/algorithm/copy) This could also likely be replaced by std::memcpy, but as said above, I believe the using std::copy is the more c++ way to do anything and is almost guaranteed to compile to the same asm - memcmp -> std::memcmp ACKs for top commit: achow101: ACK 935acdc hebasto: Approach ACK 935acdc. aureleoules: reACK 935acdc john-moffett: ACK 935acdc stickies-v: Approach ACK 935acdc Tree-SHA512: 4f1ba54ff2198eea0e505d41e73d552c84c60f6878d5c85a94a8ab57f39afc94ef8d79258e7afd01fa84ec2a99f4404bb877eecd671f65e1ee9273f3129fc650
backport: merge bitcoin#26105, bitcoin#26345 (uint256 modern)
WIDTH
instead ofsizeof(m_data)
This may also be replaced by std::memset, but I think that std::fill is more idiomatic of modern c++ and readable.
Note: In practice, implementations of std::copy avoid multiple assignments and use bulk copy functions such as std::memmove if the value type is TriviallyCopyable and the iterator types satisfy LegacyContiguousIterator. (https://en.cppreference.com/w/cpp/algorithm/copy)
This could also likely be replaced by std::memcpy, but as said above, I believe the using std::copy is the more c++ way to do anything and is almost guaranteed to compile to the same asm