Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2022-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <memusage.h>6#include <support/allocators/pool.h>7#include <test/util/poolresourcetester.h>8#include <test/util/random.h>9#include <test/util/setup_common.h>11#include <boost/test/unit_test.hpp>13#include <cstddef>14#include <cstdint>15#include <unordered_map>16#include <vector>18BOOST_FIXTURE_TEST_SUITE(pool_tests, BasicTestingSetup)20BOOST_AUTO_TEST_CASE(basic_allocating)21{22auto resource = PoolResource<8, 8>();23PoolResourceTester::CheckAllDataAccountedFor(resource);25// first chunk is already allocated26size_t expected_bytes_available = resource.ChunkSizeBytes();27BOOST_TEST(expected_bytes_available == PoolResourceTester::AvailableMemoryFromChunk(resource));29// chunk is used, no more allocation30void* block = resource.Allocate(8, 8);31expected_bytes_available -= 8;32BOOST_TEST(expected_bytes_available == PoolResourceTester::AvailableMemoryFromChunk(resource));34BOOST_TEST(0 == PoolResourceTester::FreeListSizes(resource)[1]);35resource.Deallocate(block, 8, 8);36PoolResourceTester::CheckAllDataAccountedFor(resource);37BOOST_TEST(1 == PoolResourceTester::FreeListSizes(resource)[1]);39// alignment is too small, but the best fitting freelist is used. Nothing is allocated.40void* b = resource.Allocate(8, 1);41BOOST_TEST(b == block); // we got the same block of memory as before42BOOST_TEST(0 == PoolResourceTester::FreeListSizes(resource)[1]);43BOOST_TEST(expected_bytes_available == PoolResourceTester::AvailableMemoryFromChunk(resource));45resource.Deallocate(block, 8, 1);46PoolResourceTester::CheckAllDataAccountedFor(resource);47BOOST_TEST(1 == PoolResourceTester::FreeListSizes(resource)[1]);48BOOST_TEST(expected_bytes_available == PoolResourceTester::AvailableMemoryFromChunk(resource));50// can't use resource because alignment is too big, allocate system memory51b = resource.Allocate(8, 16);52BOOST_TEST(b != block);53block = b;54PoolResourceTester::CheckAllDataAccountedFor(resource);55BOOST_TEST(1 == PoolResourceTester::FreeListSizes(resource)[1]);56BOOST_TEST(expected_bytes_available == PoolResourceTester::AvailableMemoryFromChunk(resource));58resource.Deallocate(block, 8, 16);59PoolResourceTester::CheckAllDataAccountedFor(resource);60BOOST_TEST(1 == PoolResourceTester::FreeListSizes(resource)[1]);61BOOST_TEST(expected_bytes_available == PoolResourceTester::AvailableMemoryFromChunk(resource));63// can't use chunk because size is too big64block = resource.Allocate(16, 8);65PoolResourceTester::CheckAllDataAccountedFor(resource);66BOOST_TEST(1 == PoolResourceTester::FreeListSizes(resource)[1]);67BOOST_TEST(expected_bytes_available == PoolResourceTester::AvailableMemoryFromChunk(resource));69resource.Deallocate(block, 16, 8);70PoolResourceTester::CheckAllDataAccountedFor(resource);71BOOST_TEST(1 == PoolResourceTester::FreeListSizes(resource)[1]);72BOOST_TEST(expected_bytes_available == PoolResourceTester::AvailableMemoryFromChunk(resource));74// it's possible that 0 bytes are allocated, make sure this works. In that case the call is forwarded to operator new75// 0 bytes takes one entry from the first freelist76void* p = resource.Allocate(0, 1);77BOOST_TEST(0 == PoolResourceTester::FreeListSizes(resource)[1]);78BOOST_TEST(expected_bytes_available == PoolResourceTester::AvailableMemoryFromChunk(resource));80resource.Deallocate(p, 0, 1);81PoolResourceTester::CheckAllDataAccountedFor(resource);82BOOST_TEST(1 == PoolResourceTester::FreeListSizes(resource)[1]);83BOOST_TEST(expected_bytes_available == PoolResourceTester::AvailableMemoryFromChunk(resource));84}86// Allocates from 0 to n bytes were n > the PoolResource's data, and each should work87BOOST_AUTO_TEST_CASE(allocate_any_byte)88{89auto resource = PoolResource<128, 8>(1024);91uint8_t num_allocs = 200;93auto data = std::vector<std::span<uint8_t>>();95// allocate an increasing number of bytes96for (uint8_t num_bytes = 0; num_bytes < num_allocs; ++num_bytes) {97uint8_t* bytes = new (resource.Allocate(num_bytes, 1)) uint8_t[num_bytes];98BOOST_TEST(bytes != nullptr);99data.emplace_back(bytes, num_bytes);101// set each byte to num_bytes102std::fill(bytes, bytes + num_bytes, num_bytes);103}105// now that we got all allocated, test if all still have the correct values, and give everything back to the allocator106uint8_t val = 0;107for (auto const& span : data) {108for (auto x : span) {109BOOST_TEST(val == x);110}111std::destroy(span.data(), span.data() + span.size());112resource.Deallocate(span.data(), span.size(), 1);113++val;114}116PoolResourceTester::CheckAllDataAccountedFor(resource);117}119BOOST_AUTO_TEST_CASE(random_allocations)120{121struct PtrSizeAlignment {122void* ptr;123size_t bytes;124size_t alignment;125};127// makes a bunch of random allocations and gives all of them back in random order.128auto resource = PoolResource<128, 8>(65536);129std::vector<PtrSizeAlignment> ptr_size_alignment{};130for (size_t i = 0; i < 1000; ++i) {131// make it a bit more likely to allocate than deallocate132if (ptr_size_alignment.empty() || 0 != m_rng.randrange(4)) {133// allocate a random item134std::size_t alignment = std::size_t{1} << m_rng.randrange(8); // 1, 2, ..., 128135std::size_t size = (m_rng.randrange(200) / alignment + 1) * alignment; // multiple of alignment136void* ptr = resource.Allocate(size, alignment);137BOOST_TEST(ptr != nullptr);138BOOST_TEST((reinterpret_cast<uintptr_t>(ptr) & (alignment - 1)) == 0);139ptr_size_alignment.push_back({ptr, size, alignment});140} else {141// deallocate a random item142auto& x = ptr_size_alignment[m_rng.randrange(ptr_size_alignment.size())];143resource.Deallocate(x.ptr, x.bytes, x.alignment);144x = ptr_size_alignment.back();145ptr_size_alignment.pop_back();146}147}149// deallocate all the rest150for (auto const& x : ptr_size_alignment) {151resource.Deallocate(x.ptr, x.bytes, x.alignment);152}154PoolResourceTester::CheckAllDataAccountedFor(resource);155}157BOOST_AUTO_TEST_CASE(memusage_test)158{159auto std_map = std::unordered_map<int64_t, int64_t>{};161using Map = std::unordered_map<int64_t,162int64_t,163std::hash<int64_t>,164std::equal_to<int64_t>,165PoolAllocator<std::pair<const int64_t, int64_t>,166sizeof(std::pair<const int64_t, int64_t>) + sizeof(void*) * 4>>;167auto resource = Map::allocator_type::ResourceType(1024);169PoolResourceTester::CheckAllDataAccountedFor(resource);171{172auto resource_map = Map{0, std::hash<int64_t>{}, std::equal_to<int64_t>{}, &resource};174// can't have the same resource usage175BOOST_TEST(memusage::DynamicUsage(std_map) != memusage::DynamicUsage(resource_map));177for (size_t i = 0; i < 10000; ++i) {178std_map[i];179resource_map[i];180}182// Eventually the resource_map should have a much lower memory usage because it has less malloc overhead183BOOST_TEST(memusage::DynamicUsage(resource_map) <= memusage::DynamicUsage(std_map) * 90 / 100);185// Make sure the pool is actually used by the nodes186auto max_nodes_per_chunk = resource.ChunkSizeBytes() / sizeof(Map::value_type);187auto min_num_allocated_chunks = resource_map.size() / max_nodes_per_chunk + 1;188BOOST_TEST(resource.NumAllocatedChunks() >= min_num_allocated_chunks);189}191PoolResourceTester::CheckAllDataAccountedFor(resource);192}194BOOST_AUTO_TEST_SUITE_END()
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.