This PR improves code readability.
Only safe cases with guarantee of no undefined behavior.
This PR improves code readability.
Only safe cases with guarantee of no undefined behavior.
<!--e57a25ab6845829454e8d69fc972939a-->
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
<!--174a7506f384e20aa4161008e828411d-->
Reviewers, this pull request conflicts with the following ones:
src/node/ and src/wallet/ code to node:: and wallet:: namespaces by ryanofsky)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.
Concept ACK
Concept ACK
std::clamp is standard in C++17 and is certainly a nice cleanup versus the existing code. Additionally, this is not a breaking change for windows builders as this is available since VS2015.
Concept ACK
I wonder whether this is a good candidate or not: https://github.com/bitcoin/bitcoin/blob/58c25bdcea9b95348e639180932b388d4bda8157/src/rpc/blockchain.cpp#L1859
I wonder whether this is a good candidate or not:
Some tests fail with this line being changed.
I wonder whether this is a good candidate or not: https://github.com/bitcoin/bitcoin/blob/58c25bdcea9b95348e639180932b388d4bda8157/src/rpc/blockchain.cpp#L1859
In case you think it is a good candidate for changing to std::clamp, it would be helpful to post how you changed the line and a test failure details.
In case you think it is a good candidate for changing to
std::clamp, it would be helpful to post how you changed the line and a test failure details.
~Here are the branch and tests.~
Branch: 7583dee2776b053928542cd82bf89e053cf4388d CI tests: https://cirrus-ci.com/build/4594368825786368
Ouch-if tests fail that means the behavior is not the same. I think we should be careful here. For the C++11 transition we even had an explicit rule not to do all-over-the-place code modernization like this, it's very easy to accidentally introduce a bug. I'm all for using clamp but might be better to introduce it when code is changed anyway.
This is not a trivial replacement. With std::min/max the argument order doesn't matter (min(a,b)==min(b,a)). However, with std::clamp, the order must be exactly right and there is only one correct order.
I think the test has failed because std::clamp's behavior is undefined when when the lo value is bigger than the hi value. For std::clamp(blockcount, 0, pindex->nHeight - 1) this is possible when pindex->nHeight==0
the order must be exactly right and there is only one correct order.
Not only that, there can be cases where there is no order that is always correct :)
108 | @@ -109,7 +109,7 @@ class CCheckQueue 109 | // all workers finish approximately simultaneously. 110 | // * Try to account for idle jobs which will instantly start helping. 111 | // * Don't do batches smaller than 1 (duh), or larger than nBatchSize. 112 | - nNow = std::max(1U, std::min(nBatchSize, (unsigned int)queue.size() / (nTotal + nIdle + 1))); 113 | + nNow = std::clamp<unsigned int>(queue.size() / (nTotal + nIdle + 1), 1, nBatchSize);
Not sure if this can ever happen, but when nBatchSize==0 this will now have undefined behavior. Previously the result used to be 1.
Right. Would assert(nBatchSize >= 1); in the CCheckQueue constructor be enough?
I'd put it right above the clamp, or not use clamp when there's a possibility that that could happen
I thought again, and put the assertion into the constructor because it is constructor's job to establish safe invariants for a class instance.
880 | @@ -880,7 +881,7 @@ bool AppInitParameterInteraction(const ArgsManager& args) 881 | #else 882 | int fd_max = FD_SETSIZE; 883 | #endif 884 | - nMaxConnections = std::max(std::min<int>(nMaxConnections, fd_max - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS - NUM_FDS_MESSAGE_CAPTURE), 0); 885 | + nMaxConnections = std::clamp<int>(nMaxConnections, 0, fd_max - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS - NUM_FDS_MESSAGE_CAPTURE);
If fd_max - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS - NUM_FDS_MESSAGE_CAPTURE is negative then nMaxConnections used to be 0, but now the result is undefined
Updated 82c4e71025d0958b4066325731874863e1323a0e -> bf4838e6456fa744911a886356529656a16c19ed (pr23095.04 -> pr23095.05):
Code review ACK bf4838e6456fa744911a886356529656a16c19ed
Updated bf4838e6456fa744911a886356529656a16c19ed -> 665353fa65da110a9bd2b981029bf5ba9e1b9de3 (pr23095.05 -> pr23095.06, diff):
This change improves code readability.
Updated 665353fa65da110a9bd2b981029bf5ba9e1b9de3 -> 512dcf7d5b7ff815ce4fec2d6fe27cbb13aa3e33 (pr23095.06 -> pr23095.07) due to the conflict with #23137.
63 | @@ -64,7 +64,7 @@ BlockAssembler::BlockAssembler(CChainState& chainstate, const CTxMemPool& mempoo 64 | { 65 | blockMinFeeRate = options.blockMinFeeRate; 66 | // Limit weight to between 4K and MAX_BLOCK_WEIGHT-4K for sanity: 67 | - nBlockMaxWeight = std::max<size_t>(4000, std::min<size_t>(MAX_BLOCK_WEIGHT - 4000, options.nBlockMaxWeight)); 68 | + nBlockMaxWeight = std::clamp<size_t>(options.nBlockMaxWeight, 4000, MAX_BLOCK_WEIGHT - 4000);
Can we please add a static assert that MAX_BLOCK_WEIGHT >= 8000?
Wouldn't it be better to have a clamp function that does this by default, internally and at compile time? The condition must hold for all call sites.
Though in most cases it's not possible to verify it at compile time, this would handle only the 'between two constexpr' case. In other cases there might be non-trivial error handling.
For the cases where it is not possible to verify at compile time, crashing the node seems preferable over UB. Or is this something that sanitizers can spot?
Code review ACK 512dcf7d5b7ff815ce4fec2d6fe27cbb13aa3e33 I checked all the changes and they look correct to me.
Slightly tend toward NACK. It is non-trivial to review (https://github.com/bitcoin/bitcoin/pull/23095#issuecomment-927779377) and easily introduces UB, if the second or third argument are not compile time constants (https://github.com/bitcoin/bitcoin/pull/23095#issuecomment-927780250).
It is non-trivial to review (#23095 (comment)) and easily introduces UB.
Closing.
Maybe write a Clamp<int64_t, /*min=*/-1, /*max=*/3>(var); wrapper that has static asserts built in, and another one Clamp that copies std::clamp's signature that asserts/throws/returns optional on UB?