mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-03-16 16:32:47 +00:00
refactor: add overflow-safe CeilDiv helper
Introduce `CeilDiv()` for integral ceiling division without the typical `(dividend + divisor - 1) / divisor` overflow, asserting a non-zero divisor. Replace existing ceiling-division expressions with `CeilDiv()` to centralize the preconditions. Add unit tests covering return type deduction, max-value behavior, and divisor checks.
This commit is contained in:
parent
4a05825a3f
commit
02d047fd5b
@ -5,8 +5,9 @@
|
||||
|
||||
#include <arith_uint256.h>
|
||||
|
||||
#include <uint256.h>
|
||||
#include <crypto/common.h>
|
||||
#include <uint256.h>
|
||||
#include <util/overflow.h>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
@ -194,7 +195,7 @@ arith_uint256& arith_uint256::SetCompact(uint32_t nCompact, bool* pfNegative, bo
|
||||
|
||||
uint32_t arith_uint256::GetCompact(bool fNegative) const
|
||||
{
|
||||
int nSize = (bits() + 7) / 8;
|
||||
int nSize = CeilDiv(bits(), 8u);
|
||||
uint32_t nCompact = 0;
|
||||
if (nSize <= 3) {
|
||||
nCompact = GetLow64() << 8 * (3 - nSize);
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
#include <span.h>
|
||||
#include <streams.h>
|
||||
#include <util/fastrange.h>
|
||||
#include <util/overflow.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
@ -166,7 +167,7 @@ CRollingBloomFilter::CRollingBloomFilter(const unsigned int nElements, const dou
|
||||
* restrict it to the range 1-50. */
|
||||
nHashFuncs = std::max(1, std::min((int)round(logFpRate / log(0.5)), 50));
|
||||
/* In this rolling bloom filter, we'll store between 2 and 3 generations of nElements / 2 entries. */
|
||||
nEntriesPerGeneration = (nElements + 1) / 2;
|
||||
nEntriesPerGeneration = CeilDiv(nElements, 2u);
|
||||
uint32_t nMaxElements = nEntriesPerGeneration * 3;
|
||||
/* The maximum fpRate = pow(1.0 - exp(-nHashFuncs * nMaxElements / nFilterBits), nHashFuncs)
|
||||
* => pow(fpRate, 1.0 / nHashFuncs) = 1.0 - exp(-nHashFuncs * nMaxElements / nFilterBits)
|
||||
@ -182,7 +183,7 @@ CRollingBloomFilter::CRollingBloomFilter(const unsigned int nElements, const dou
|
||||
* treated as set in generation 1, 2, or 3 respectively.
|
||||
* These bits are stored in separate integers: position P corresponds to bit
|
||||
* (P & 63) of the integers data[(P >> 6) * 2] and data[(P >> 6) * 2 + 1]. */
|
||||
data.resize(((nFilterBits + 63) / 64) << 1);
|
||||
data.resize(CeilDiv(nFilterBits, 64u) << 1);
|
||||
reset();
|
||||
}
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#define BITCOIN_CUCKOOCACHE_H
|
||||
|
||||
#include <util/fastrange.h>
|
||||
#include <util/overflow.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
@ -63,7 +64,7 @@ public:
|
||||
explicit bit_packed_atomic_flags(uint32_t size)
|
||||
{
|
||||
// pad out the size if needed
|
||||
size = (size + 7) / 8;
|
||||
size = CeilDiv(size, 8u);
|
||||
mem.reset(new std::atomic<uint8_t>[size]);
|
||||
for (uint32_t i = 0; i < size; ++i)
|
||||
mem[i].store(0xFF);
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include <tinyformat.h>
|
||||
#include <util/fs_helpers.h>
|
||||
#include <util/log.h>
|
||||
#include <util/overflow.h>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
@ -59,8 +60,8 @@ size_t FlatFileSeq::Allocate(const FlatFilePos& pos, size_t add_size, bool& out_
|
||||
{
|
||||
out_of_space = false;
|
||||
|
||||
unsigned int n_old_chunks = (pos.nPos + m_chunk_size - 1) / m_chunk_size;
|
||||
unsigned int n_new_chunks = (pos.nPos + add_size + m_chunk_size - 1) / m_chunk_size;
|
||||
unsigned int n_old_chunks = CeilDiv(pos.nPos, m_chunk_size);
|
||||
unsigned int n_new_chunks = CeilDiv(pos.nPos + add_size, m_chunk_size);
|
||||
if (n_new_chunks > n_old_chunks) {
|
||||
size_t old_size = pos.nPos;
|
||||
size_t new_size = n_new_chunks * m_chunk_size;
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
#include <script/interpreter.h>
|
||||
#include <script/solver.h>
|
||||
#include <tinyformat.h>
|
||||
#include <util/overflow.h>
|
||||
#include <util/strencodings.h>
|
||||
|
||||
#include <algorithm>
|
||||
@ -72,7 +73,7 @@ public:
|
||||
return {};
|
||||
}
|
||||
std::vector<unsigned char> data = {(unsigned char)id.GetWitnessVersion()};
|
||||
data.reserve(1 + (program.size() * 8 + 4) / 5);
|
||||
data.reserve(1 + CeilDiv(program.size() * 8, 5u));
|
||||
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, program.begin(), program.end());
|
||||
return bech32::Encode(bech32::Encoding::BECH32M, m_params.Bech32HRP(), data);
|
||||
}
|
||||
|
||||
@ -5,13 +5,14 @@
|
||||
|
||||
#include <merkleblock.h>
|
||||
|
||||
#include <hash.h>
|
||||
#include <consensus/consensus.h>
|
||||
#include <hash.h>
|
||||
#include <util/overflow.h>
|
||||
|
||||
|
||||
std::vector<unsigned char> BitsToBytes(const std::vector<bool>& bits)
|
||||
{
|
||||
std::vector<unsigned char> ret((bits.size() + 7) / 8);
|
||||
std::vector<unsigned char> ret(CeilDiv(bits.size(), 8u));
|
||||
for (unsigned int p = 0; p < bits.size(); p++) {
|
||||
ret[p / 8] |= bits[p] << (p % 8);
|
||||
}
|
||||
@ -174,7 +175,7 @@ uint256 CPartialMerkleTree::ExtractMatches(std::vector<Txid> &vMatch, std::vecto
|
||||
if (fBad)
|
||||
return uint256();
|
||||
// verify that all bits were consumed (except for the padding caused by serializing it as a byte sequence)
|
||||
if ((nBitsUsed+7)/8 != (vBits.size()+7)/8)
|
||||
if (CeilDiv(nBitsUsed, 8u) != CeilDiv(vBits.size(), 8u))
|
||||
return uint256();
|
||||
// verify that all hashes were consumed
|
||||
if (nHashUsed != vHash.size())
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
#include <undo.h>
|
||||
#include <util/any.h>
|
||||
#include <util/check.h>
|
||||
#include <util/overflow.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <validation.h>
|
||||
|
||||
@ -993,7 +994,7 @@ static bool rest_getutxos(const std::any& context, HTTPRequest* req, const std::
|
||||
std::vector<CCoin> outs;
|
||||
std::string bitmapStringRepresentation;
|
||||
std::vector<bool> hits;
|
||||
bitmap.resize((vOutPoints.size() + 7) / 8);
|
||||
bitmap.resize(CeilDiv(vOutPoints.size(), 8u));
|
||||
ChainstateManager* maybe_chainman = GetChainman(context, req);
|
||||
if (!maybe_chainman) return false;
|
||||
ChainstateManager& chainman = *maybe_chainman;
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#include <compat/endian.h>
|
||||
#include <prevector.h>
|
||||
#include <span.h>
|
||||
#include <util/overflow.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <concepts>
|
||||
@ -425,7 +426,7 @@ template<typename Stream, VarIntMode Mode, typename I>
|
||||
void WriteVarInt(Stream& os, I n)
|
||||
{
|
||||
CheckVarIntMode<Mode, I>();
|
||||
unsigned char tmp[(sizeof(n)*8+6)/7];
|
||||
unsigned char tmp[CeilDiv(sizeof(n) * 8, 7u)];
|
||||
int len=0;
|
||||
while(true) {
|
||||
tmp[len] = (n & 0x7F) | (len ? 0x80 : 0x00);
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include <util/check.h>
|
||||
#include <util/overflow.h>
|
||||
|
||||
/**
|
||||
* A memory resource similar to std::pmr::unsynchronized_pool_resource, but
|
||||
@ -127,7 +128,7 @@ class PoolResource final
|
||||
*/
|
||||
[[nodiscard]] static constexpr std::size_t NumElemAlignBytes(std::size_t bytes)
|
||||
{
|
||||
return (bytes + ELEM_ALIGN_BYTES - 1) / ELEM_ALIGN_BYTES + (bytes == 0);
|
||||
return CeilDiv(bytes, ELEM_ALIGN_BYTES) + (bytes == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1833,4 +1833,41 @@ BOOST_AUTO_TEST_CASE(mib_string_literal_test)
|
||||
BOOST_CHECK_EXCEPTION(operator""_MiB(static_cast<unsigned long long>(max_mib) + 1), std::overflow_error, HasReason("MiB value too large for size_t byte conversion"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ceil_div_test)
|
||||
{
|
||||
// Type combinations used by current CeilDiv callsites.
|
||||
BOOST_CHECK((std::is_same_v<decltype(CeilDiv(uint32_t{0}, 8u)), uint32_t>));
|
||||
BOOST_CHECK((std::is_same_v<decltype(CeilDiv(size_t{0}, 8u)), size_t>));
|
||||
BOOST_CHECK((std::is_same_v<decltype(CeilDiv(unsigned{0}, size_t{1})), size_t>));
|
||||
|
||||
// `common/bloom.cpp` and `cuckoocache.h` patterns.
|
||||
BOOST_CHECK_EQUAL(CeilDiv(uint32_t{3}, 2u), uint32_t{2});
|
||||
BOOST_CHECK_EQUAL(CeilDiv(uint32_t{65}, 64u), uint32_t{2});
|
||||
BOOST_CHECK_EQUAL(CeilDiv(uint32_t{9}, 8u), uint32_t{2});
|
||||
|
||||
// `key_io.cpp`, `rest.cpp`, `merkleblock.cpp`, `strencodings.cpp` patterns.
|
||||
BOOST_CHECK_EQUAL(CeilDiv(size_t{9}, 8u), size_t{2});
|
||||
BOOST_CHECK_EQUAL(CeilDiv(size_t{10}, 3u), size_t{4});
|
||||
BOOST_CHECK_EQUAL(CeilDiv(size_t{11}, 5u), size_t{3});
|
||||
BOOST_CHECK_EQUAL(CeilDiv(size_t{41} * 8, 5u), size_t{66});
|
||||
|
||||
// `flatfile.cpp` mixed unsigned/size_t pattern.
|
||||
BOOST_CHECK_EQUAL(CeilDiv(unsigned{10}, size_t{4}), size_t{3});
|
||||
|
||||
// `util/feefrac.h` fast-path rounding-up pattern.
|
||||
constexpr int64_t fee{12345};
|
||||
constexpr int32_t at_size{67};
|
||||
constexpr int32_t size{10};
|
||||
BOOST_CHECK_EQUAL(CeilDiv(uint64_t(fee) * at_size, uint32_t(size)),
|
||||
(uint64_t(fee) * at_size + uint32_t(size) - 1) / uint32_t(size));
|
||||
|
||||
// `bitset.h` template parameter pattern.
|
||||
constexpr unsigned bits{129};
|
||||
constexpr size_t digits{std::numeric_limits<size_t>::digits};
|
||||
BOOST_CHECK_EQUAL(CeilDiv(bits, digits), (bits + digits - 1) / digits);
|
||||
|
||||
// `serialize.h` varint scratch-buffer pattern.
|
||||
BOOST_CHECK_EQUAL(CeilDiv(sizeof(uint64_t) * 8, 7u), (sizeof(uint64_t) * 8 + 6) / 7);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#ifndef BITCOIN_UTIL_OVERFLOW_H
|
||||
#define BITCOIN_UTIL_OVERFLOW_H
|
||||
|
||||
#include <cassert>
|
||||
#include <climits>
|
||||
#include <concepts>
|
||||
#include <limits>
|
||||
@ -49,6 +50,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.
|
||||
|
||||
@ -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) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user