util: add _GiB for Gibibyte conversions
What changed, and why it matters
This commit adds a new code helper for writing byte sizes in gigabytes (GiB) and replaces a few existing hard-coded numbers with the new helper. It is a code cleanup and readability change with no security-relevant behavior change.
No security action required. Treat as a normal refactoring/review change.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit introduces operator""_GiB in src/util/byte_units.h, reusing the existing overflow-checked left-shift helper that already backs operator""_MiB. It then replaces literal expressions such as 1024_MiB, 1<<30, and 4096ULL << 20 with 1_GiB or 4 * 1_GiB in src/init.cpp, src/node/caches.cpp, and src/node/caches.h. Unit tests are extended to cover the new literal and its overflow boundary. The semantics of the replaced expressions are identical to the originals, and the overflow checking is unchanged.
Changed components
src/util/byte_units.hsrc/init.cppsrc/node/caches.cppsrc/node/caches.hsrc/test/caches_tests.cppsrc/test/util_tests.cppInspect captured patch +77 / −28
diff --git a/src/init.cpp b/src/init.cpp
index 407ee0fe..4b6e00bd 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1972,7 +1972,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// On first startup, warn on low block storage space
if (!do_reindex && !do_reindex_chainstate && chain_active_height <= 1) {
- uint64_t assumed_chain_bytes{chainparams.AssumedBlockchainSize() * 1024 * 1024 * 1024};
+ uint64_t assumed_chain_bytes{chainparams.AssumedBlockchainSize() * 1_GiB};
uint64_t additional_bytes_needed{
chainman.m_blockman.IsPruneMode() ?
std::min(chainman.m_blockman.GetPruneTarget(), assumed_chain_bytes) :
diff --git a/src/node/caches.cpp b/src/node/caches.cpp
index cb8afbc9..3b9e609f 100644
--- a/src/node/caches.cpp
+++ b/src/node/caches.cpp
@@ -20,17 +20,17 @@
// Unlike for the UTXO database, for the txindex scenario the leveldb cache make
// a meaningful difference: https://github.com/bitcoin/bitcoin/pull/8273#issuecomment-229601991
//! Max memory allocated to tx index DB specific cache in bytes.
-static constexpr size_t MAX_TX_INDEX_CACHE{1024_MiB};
+static constexpr size_t MAX_TX_INDEX_CACHE{1_GiB};
//! Max memory allocated to all block filter index caches combined in bytes.
-static constexpr size_t MAX_FILTER_INDEX_CACHE{1024_MiB};
+static constexpr size_t MAX_FILTER_INDEX_CACHE{1_GiB};
//! Max memory allocated to tx spenderindex DB specific cache in bytes.
-static constexpr size_t MAX_TXOSPENDER_INDEX_CACHE{1024_MiB};
+static constexpr size_t MAX_TXOSPENDER_INDEX_CACHE{1_GiB};
//! Maximum dbcache size on 32-bit systems.
-static constexpr size_t MAX_32BIT_DBCACHE{1024_MiB};
+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{1024_MiB};
+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{4096ULL << 20};
+static constexpr uint64_t HIGH_DEFAULT_DBCACHE_MIN_TOTAL_RAM{uint64_t{4} * 1_GiB};
namespace node {
size_t GetDefaultDBCache()
diff --git a/src/node/caches.h b/src/node/caches.h
index 62f09741..483521df 100644
--- a/src/node/caches.h
+++ b/src/node/caches.h
@@ -31,7 +31,7 @@ struct CacheSizes {
CacheSizes CalculateCacheSizes(const ArgsManager& args, size_t n_indexes = 0);
constexpr bool ShouldWarnOversizedDbCache(size_t dbcache, size_t total_ram) noexcept
{
- const size_t cap{(total_ram < 2048_MiB) ? DEFAULT_DB_CACHE : (total_ram / 100) * 75};
+ const size_t cap{(total_ram < 2_GiB) ? DEFAULT_DB_CACHE : (total_ram / 100) * 75};
return dbcache > cap;
}
diff --git a/src/test/caches_tests.cpp b/src/test/caches_tests.cpp
index f444f1be..bdca1d40 100644
--- a/src/test/caches_tests.cpp
+++ b/src/test/caches_tests.cpp
@@ -14,30 +14,30 @@ BOOST_AUTO_TEST_SUITE(caches_tests)
BOOST_AUTO_TEST_CASE(oversized_dbcache_warning)
{
// memory restricted setup - cap is DEFAULT_DB_CACHE (450 MiB)
- BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/4_MiB, /*total_ram=*/1024_MiB)); // Under cap
- BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/512_MiB, /*total_ram=*/1024_MiB)); // At cap
- BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/1500_MiB, /*total_ram=*/1024_MiB)); // Over cap
+ BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/4_MiB, /*total_ram=*/1_GiB)); // Under cap
+ BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/512_MiB, /*total_ram=*/1_GiB)); // At cap
+ BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/1500_MiB, /*total_ram=*/1_GiB)); // Over cap
// 2 GiB RAM - cap is 75%
- BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/1500_MiB, /*total_ram=*/2048_MiB)); // Under cap
- BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/1600_MiB, /*total_ram=*/2048_MiB)); // Over cap
+ BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/1500_MiB, /*total_ram=*/2_GiB)); // Under cap
+ BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/1600_MiB, /*total_ram=*/2_GiB)); // Over cap
if constexpr (SIZE_MAX == UINT64_MAX) {
// 4 GiB RAM - cap is 75%
- BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/2500_MiB, /*total_ram=*/4096_MiB)); // Under cap
- BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/3500_MiB, /*total_ram=*/4096_MiB)); // Over cap
+ BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/2500_MiB, /*total_ram=*/4_GiB)); // Under cap
+ BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/3500_MiB, /*total_ram=*/4_GiB)); // Over cap
// 8 GiB RAM - cap is 75%
- BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/6000_MiB, /*total_ram=*/8192_MiB)); // Under cap
- BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/7000_MiB, /*total_ram=*/8192_MiB)); // Over cap
+ BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/6000_MiB, /*total_ram=*/8_GiB)); // Under cap
+ BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/7000_MiB, /*total_ram=*/8_GiB)); // Over cap
// 16 GiB RAM - cap is 75%
- BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/10'000_MiB, /*total_ram=*/16384_MiB)); // Under cap
- BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/15'000_MiB, /*total_ram=*/16384_MiB)); // Over cap
+ BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/10'000_MiB, /*total_ram=*/16_GiB)); // Under cap
+ BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/15'000_MiB, /*total_ram=*/16_GiB)); // Over cap
// 32 GiB RAM - cap is 75%
- BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/20'000_MiB, /*total_ram=*/32768_MiB)); // Under cap
- BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/30'000_MiB, /*total_ram=*/32768_MiB)); // Over cap
+ BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/20'000_MiB, /*total_ram=*/32_GiB)); // Under cap
+ BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/30'000_MiB, /*total_ram=*/32_GiB)); // Over cap
}
}
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index dc6a5cf3..796a9deb 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -1636,7 +1636,7 @@ BOOST_AUTO_TEST_CASE(util_ParseByteUnits)
BOOST_CHECK_EQUAL(ParseByteUnits("2M", noop).value(), 2ULL << 20);
BOOST_CHECK_EQUAL(ParseByteUnits("3g", noop).value(), 3'000'000'000ULL);
- BOOST_CHECK_EQUAL(ParseByteUnits("3G", noop).value(), 3ULL << 30);
+ BOOST_CHECK_EQUAL(ParseByteUnits("3G", noop).value(), 3_GiB);
BOOST_CHECK_EQUAL(ParseByteUnits("4t", noop).value(), 4'000'000'000'000ULL);
BOOST_CHECK_EQUAL(ParseByteUnits("4T", noop).value(), 4ULL << 40);
@@ -1829,10 +1829,11 @@ BOOST_AUTO_TEST_CASE(saturating_left_shift_test)
BOOST_AUTO_TEST_CASE(mib_string_literal_test)
{
+ // Basic equivalences and simple arithmetic operations
BOOST_CHECK_EQUAL(0_MiB, 0);
BOOST_CHECK_EQUAL(1_MiB, 1024 * 1024);
- const auto max_mib{std::numeric_limits<size_t>::max() >> 20};
- 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"));
+ constexpr auto max_mib{std::numeric_limits<size_t>::max() >> 20};
+ BOOST_CHECK_EXCEPTION(operator""_MiB(max_mib + 1), std::overflow_error, HasReason("MiB value too large for size_t byte conversion"));
}
BOOST_AUTO_TEST_CASE(ceil_div_test)
@@ -1872,4 +1873,37 @@ BOOST_AUTO_TEST_CASE(ceil_div_test)
BOOST_CHECK_EQUAL(CeilDiv(sizeof(uint64_t) * 8, 7u), (sizeof(uint64_t) * 8 + 6) / 7);
}
+BOOST_AUTO_TEST_CASE(gib_string_literal_test)
+{
+ // Basic equivalences and simple arithmetic operations
+ BOOST_CHECK_EQUAL(0_GiB, 0);
+ BOOST_CHECK_EQUAL(1_GiB, 1 << 30);
+ BOOST_CHECK_EQUAL(1_GiB, 1024 * 1024 * 1024);
+ BOOST_CHECK_EQUAL(1_GiB, 0x40000000U);
+ BOOST_CHECK_EQUAL(1_GiB, 1073741824U);
+ BOOST_CHECK_EQUAL(1_GiB, 1_MiB * 1024);
+ BOOST_CHECK_EQUAL(1_GiB, 1024_MiB);
+ BOOST_CHECK_EQUAL((1_GiB + 123) / double(1_GiB), (1_GiB + 123) / 1024.0 / 1024.0 / 1024.0);
+ BOOST_CHECK_EQUAL(2ULL * 1_GiB, 2ULL << 30);
+ BOOST_CHECK_EQUAL(4 * uint64_t{1_GiB}, uint64_t{4} << 30);
+ BOOST_CHECK_EQUAL(2_GiB, 2048_MiB);
+ 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"));
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/util/byte_units.h b/src/util/byte_units.h
index 3d6a9627..b67af5a4 100644
--- a/src/util/byte_units.h
+++ b/src/util/byte_units.h
@@ -7,16 +7,31 @@
#include <util/overflow.h>
+#include <limits>
#include <stdexcept>
-//! Overflow-safe conversion of MiB to bytes.
-constexpr size_t operator""_MiB(unsigned long long mebibytes)
+namespace util::detail {
+template <unsigned SHIFT>
+constexpr size_t ByteUnitsToBytes(unsigned long long units, const char* exception_msg)
{
- auto bytes{CheckedLeftShift(mebibytes, 20)};
+ const auto bytes{CheckedLeftShift(units, SHIFT)};
if (!bytes || *bytes > std::numeric_limits<size_t>::max()) {
- throw std::overflow_error("MiB value too large for size_t byte conversion");
+ throw std::overflow_error(exception_msg);
}
return *bytes;
}
+} // namespace util::detail
+
+//! Overflow-safe conversion of MiB to bytes.
+constexpr size_t operator""_MiB(unsigned long long mebibytes)
+{
+ return util::detail::ByteUnitsToBytes<20>(mebibytes, "MiB value too large for size_t byte conversion");
+}
+
+//! Overflow-safe conversion of GiB to bytes.
+constexpr size_t operator""_GiB(unsigned long long gibibytes)
+{
+ return util::detail::ByteUnitsToBytes<30>(gibibytes, "GiB value too large for size_t byte conversion");
+}
#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.