util: Return uint64_t from _MiB and _GiB operators
What changed, and why it matters
This is a routine code-quality change in Bitcoin Core. It changes two helper functions that convert 'MiB' and 'GiB' values into byte counts so they always return a 64-bit unsigned integer instead of a size type that can be 32-bit on some platforms. It also removes runtime overflow exceptions for values that used to be too large on 32-bit systems. There is no security bug being fixed here.
No security action required. Review as normal code-quality/refactoring change.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit modifies src/util/byte_units.h so the user-defined literals _MiB and _GiB return uint64_t rather than size_t. The helper ByteUnitsToBytes is made consteval and drops the size_t ceiling and per-literal exception messages. Call sites and unit tests are updated accordingly. The change is architectural/cleanup: it widens the return type and removes 32-bit-specific overflow checks, relying instead on compile-time evaluation and the natural uint64_t limit.
Changed components
src/util/byte_units.hsrc/node/caches.cppsrc/test/util_tests.cppInspect captured patch +33 / −35
diff --git a/src/node/caches.cpp b/src/node/caches.cpp
index 3b9e609f..3250c51a 100644
--- a/src/node/caches.cpp
+++ b/src/node/caches.cpp
@@ -30,7 +30,7 @@ static constexpr size_t MAX_32BIT_DBCACHE{1_GiB};
//! Larger default dbcache on 64-bit systems with enough RAM.
static constexpr size_t HIGH_DEFAULT_DBCACHE{1_GiB};
//! Minimum detected RAM required for HIGH_DEFAULT_DBCACHE.
-static constexpr uint64_t HIGH_DEFAULT_DBCACHE_MIN_TOTAL_RAM{uint64_t{4} * 1_GiB};
+static constexpr uint64_t HIGH_DEFAULT_DBCACHE_MIN_TOTAL_RAM{4_GiB};
namespace node {
size_t GetDefaultDBCache()
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index a407d7d3..5747c3b0 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -14,6 +14,7 @@
#include <test/util/setup_common.h>
#include <test/util/time.h>
#include <uint256.h>
+#include <univalue.h>
#include <util/bitdeque.h>
#include <util/byte_units.h>
#include <util/fs.h>
@@ -36,7 +37,7 @@
#include <optional>
#include <string>
#include <thread>
-#include <univalue.h>
+#include <type_traits>
#include <utility>
#include <vector>
@@ -1827,6 +1828,9 @@ BOOST_AUTO_TEST_CASE(saturating_left_shift_test)
TestSaturatingLeftShift<int64_t>();
}
+template <class Int, auto bytes>
+concept BraceInitializesTo = requires { Int{bytes}; };
+
BOOST_AUTO_TEST_CASE(mib_string_literal_test)
{
// Basic equivalences and simple arithmetic operations
@@ -1850,16 +1854,12 @@ BOOST_AUTO_TEST_CASE(mib_string_literal_test)
BOOST_CHECK_EQUAL(128_MiB, 0x8000000U);
BOOST_CHECK_EQUAL(550_MiB, 550ULL * 1024 * 1024);
- // Overflow handling
- constexpr auto max_mib{std::numeric_limits<size_t>::max() >> 20};
- if constexpr (SIZE_MAX == UINT32_MAX) {
- BOOST_CHECK_EQUAL(max_mib, 4095U);
- BOOST_CHECK_EQUAL(4095_MiB, size_t{4095} << 20);
- BOOST_CHECK_EXCEPTION(4096_MiB, std::overflow_error, HasReason("MiB value too large for size_t byte conversion"));
- } else {
- BOOST_CHECK_EQUAL(4096_MiB, size_t{4096} << 20);
- }
- BOOST_CHECK_EXCEPTION(operator""_MiB(max_mib + 1), std::overflow_error, HasReason("MiB value too large for size_t byte conversion"));
+ // 4095 MiB fits in uint32_t bytes. 4096 MiB requires the uint64_t return type.
+ static_assert(BraceInitializesTo<uint32_t, 4095_MiB>);
+ static_assert(!BraceInitializesTo<uint32_t, 4096_MiB>);
+ static_assert(BraceInitializesTo<uint64_t, 4096_MiB>);
+ BOOST_CHECK_EQUAL(4095_MiB, uint32_t{4095} << 20);
+ BOOST_CHECK_EQUAL(4096_MiB, uint64_t{4096} << 20);
}
BOOST_AUTO_TEST_CASE(ceil_div_test)
@@ -1916,20 +1916,18 @@ BOOST_AUTO_TEST_CASE(gib_string_literal_test)
BOOST_CHECK_EQUAL(3_GiB / 1_GiB, 3U);
BOOST_CHECK_EQUAL(3_GiB, 3U << 30);
- // Overflow handling and specific codebase values
- constexpr auto max_gib{std::numeric_limits<size_t>::max() >> 30};
- if constexpr (SIZE_MAX == UINT32_MAX) {
- BOOST_CHECK_EQUAL(max_gib, 3U);
- BOOST_CHECK_EXCEPTION(4_GiB, std::overflow_error, HasReason("GiB value too large for size_t byte conversion"));
- } else {
- BOOST_CHECK_GT(max_gib, 3U);
- BOOST_CHECK_EQUAL(4_GiB, size_t{4} << 30);
- BOOST_CHECK_EQUAL(4_GiB, 4096_MiB);
- BOOST_CHECK_EQUAL(8_GiB, 8192_MiB);
- BOOST_CHECK_EQUAL(16_GiB, 16384_MiB);
- BOOST_CHECK_EQUAL(32_GiB, 32768_MiB);
- }
- BOOST_CHECK_EXCEPTION(operator""_GiB(max_gib + 1), std::overflow_error, HasReason("GiB value too large for size_t byte conversion"));
+ // 3 GiB fits in uint32_t bytes. 4 GiB requires the uint64_t return type.
+ static_assert(BraceInitializesTo<uint32_t, 3_GiB>);
+ static_assert(!BraceInitializesTo<uint32_t, 4_GiB>);
+ static_assert(BraceInitializesTo<uint64_t, 4_GiB>);
+ BOOST_CHECK_EQUAL(3_GiB, uint32_t{3} << 30);
+ BOOST_CHECK_EQUAL(4_GiB, uint64_t{4} << 30);
+
+ // Specific codebase values
+ BOOST_CHECK_EQUAL(4_GiB, 4096_MiB);
+ BOOST_CHECK_EQUAL(8_GiB, 8192_MiB);
+ BOOST_CHECK_EQUAL(16_GiB, 16384_MiB);
+ BOOST_CHECK_EQUAL(32_GiB, 32768_MiB);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/util/byte_units.h b/src/util/byte_units.h
index b67af5a4..27d13b08 100644
--- a/src/util/byte_units.h
+++ b/src/util/byte_units.h
@@ -12,26 +12,26 @@
namespace util::detail {
template <unsigned SHIFT>
-constexpr size_t ByteUnitsToBytes(unsigned long long units, const char* exception_msg)
+consteval uint64_t ByteUnitsToBytes(unsigned long long units)
{
const auto bytes{CheckedLeftShift(units, SHIFT)};
- if (!bytes || *bytes > std::numeric_limits<size_t>::max()) {
- throw std::overflow_error(exception_msg);
+ if (!bytes || *bytes > std::numeric_limits<uint64_t>::max()) {
+ throw std::overflow_error("Too large");
}
return *bytes;
}
} // namespace util::detail
-//! Overflow-safe conversion of MiB to bytes.
-constexpr size_t operator""_MiB(unsigned long long mebibytes)
+/// Conversion of MiB to bytes.
+consteval uint64_t operator""_MiB(unsigned long long mebibytes)
{
- return util::detail::ByteUnitsToBytes<20>(mebibytes, "MiB value too large for size_t byte conversion");
+ return util::detail::ByteUnitsToBytes<20>(mebibytes);
}
-//! Overflow-safe conversion of GiB to bytes.
-constexpr size_t operator""_GiB(unsigned long long gibibytes)
+/// Conversion of GiB to bytes.
+consteval uint64_t operator""_GiB(unsigned long long gibibytes)
{
- return util::detail::ByteUnitsToBytes<30>(gibibytes, "GiB value too large for size_t byte conversion");
+ return util::detail::ByteUnitsToBytes<30>(gibibytes);
}
#endif // BITCOIN_UTIL_BYTE_UNITS_H
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.