Merge bitcoin/bitcoin#34436: refactor: add overflow-safe CeilDiv helper and use it in unsigned callsites

02d047fd5b refactor: add overflow-safe `CeilDiv` helper (Lőrinc)

Pull request description:

  ### Problem
  The codebase has many open-coded ceiling-division expressions (for example `(x+y-1)/y`) scattered across files.
  These are less readable, duplicate logic, and can be overflow-prone in edge cases.

  ### Fix
  Introduce a small overflow-safe integer helper, `CeilDiv()`, and use it in existing **unsigned** callsites where the conversion is straightforward and noise-free.

  ### What this PR does
  * Adds `CeilDiv()` to `src/util/overflow.h` for unsigned integral inputs.
  * Keeps the precondition check `assert(divisor > 0)`.
  * Replaces selected unsigned ceiling-division expressions with `CeilDiv(...)`.
  * Adds focused unit tests in `src/test/util_tests.cpp` for the migrated patterns.

  ---

  This is a pure refactor with no intended behavioral change.
  Signed arithmetic callsites are intentionally left unchanged in this PR.
  This PR changed a few more things originally but based on feedback reverted to the simplest cases only.

ACKs for top commit:
  rustaceanrob:
    ACK 02d047fd5b
  hodlinator:
    ACK 02d047fd5b
  sedited:
    ACK 02d047fd5b

Tree-SHA512: b09336031f487e6ce289822e0ffeb8cfc8cfe8a2f4f3f49470748dfbd0a6cbab97498674cb8686dd2bd4ab6dd0b79cfdf2da00041fee12d109892e1bc5dde0ff
This commit is contained in:
merge-script
2026-03-11 11:30:42 +01:00
15 changed files with 85 additions and 19 deletions

View File

@@ -11,6 +11,7 @@
#include <limits>
#include <stdexcept>
#include <tuple>
#include <util/overflow.h>
/** Class that mimics std::deque<bool>, but with std::vector<bool>'s bit packing.
*
@@ -211,7 +212,7 @@ public:
void assign(size_type count, bool val)
{
m_deque.clear();
m_deque.resize((count + BITS_PER_WORD - 1) / BITS_PER_WORD);
m_deque.resize(CeilDiv(count, size_type{BITS_PER_WORD}));
m_pad_begin = 0;
m_pad_end = 0;
if (val) {

View File

@@ -6,6 +6,7 @@
#define BITCOIN_UTIL_BITSET_H
#include <util/check.h>
#include <util/overflow.h>
#include <array>
#include <bit>
@@ -522,6 +523,6 @@ public:
template<unsigned BITS>
using BitSet = std::conditional_t<(BITS <= 32), bitset_detail::IntBitSet<uint32_t>,
std::conditional_t<(BITS <= std::numeric_limits<size_t>::digits), bitset_detail::IntBitSet<size_t>,
bitset_detail::MultiIntBitSet<size_t, (BITS + std::numeric_limits<size_t>::digits - 1) / std::numeric_limits<size_t>::digits>>>;
bitset_detail::MultiIntBitSet<size_t, CeilDiv(BITS, size_t{std::numeric_limits<size_t>::digits})>>>;
#endif // BITCOIN_UTIL_BITSET_H

View File

@@ -7,6 +7,7 @@
#include <span.h>
#include <util/check.h>
#include <util/overflow.h>
#include <compare>
#include <cstdint>
@@ -208,7 +209,7 @@ struct FeeFrac
if constexpr (RoundDown) {
return (uint64_t(fee) * at_size) / uint32_t(size);
} else {
return (uint64_t(fee) * at_size + size - 1U) / uint32_t(size);
return CeilDiv(uint64_t(fee) * at_size, uint32_t(size));
}
} else {
// Otherwise, use Mul and Div.

View File

@@ -5,6 +5,7 @@
#ifndef BITCOIN_UTIL_OVERFLOW_H
#define BITCOIN_UTIL_OVERFLOW_H
#include <cassert>
#include <climits>
#include <concepts>
#include <limits>
@@ -57,6 +58,21 @@ template <class T>
return i + j;
}
/**
* @brief Integer ceiling division (for unsigned values).
*
* Computes the smallest integer q such that q * divisor >= dividend.
* Both dividend and divisor must be unsigned, and divisor must be non-zero.
*
* The implementation avoids overflow that can occur with `(dividend + divisor - 1) / divisor`.
*/
template <std::unsigned_integral Dividend, std::unsigned_integral Divisor>
[[nodiscard]] constexpr auto CeilDiv(const Dividend dividend, const Divisor divisor)
{
assert(divisor > 0);
return dividend / divisor + (dividend % divisor != 0);
}
/**
* @brief Left bit shift with overflow checking.
* @param input The input value to be left shifted.

View File

@@ -7,6 +7,7 @@
#include <crypto/hex_base.h>
#include <span.h>
#include <util/overflow.h>
#include <array>
#include <cassert>
@@ -100,7 +101,7 @@ std::string EncodeBase64(std::span<const unsigned char> input)
static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
std::string str;
str.reserve(((input.size() + 2) / 3) * 4);
str.reserve(CeilDiv(input.size(), 3u) * 4);
ConvertBits<8, 6, true>([&](int v) { str += pbase64[v]; }, input.begin(), input.end());
while (str.size() % 4) str += '=';
return str;
@@ -146,7 +147,7 @@ std::string EncodeBase32(std::span<const unsigned char> input, bool pad)
static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567";
std::string str;
str.reserve(((input.size() + 4) / 5) * 8);
str.reserve(CeilDiv(input.size(), 5u) * 8);
ConvertBits<8, 5, true>([&](int v) { str += pbase32[v]; }, input.begin(), input.end());
if (pad) {
while (str.size() % 8) {