Inside Bitcoin
Opening the selected file…
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.
251}253/** invalid returns a special index that can never be inserted to254* @returns the special constexpr index that can never be inserted to */255constexpr uint32_t invalid() const256{257return ~(uint32_t)0;258}260/** allow_erase marks the element at index `n` as discardable. Threadsafe261* without any concurrent insert.262* @param n the index to allow erasure of263*/264inline void allow_erase(uint32_t n) const265{266collection_flags.bit_set(n);267}269/** please_keep marks the element at index `n` as an entry that should be kept.270* Threadsafe without any concurrent insert.271* @param n the index to prioritize keeping272*/273inline void please_keep(uint32_t n) const274{275collection_flags.bit_unset(n);276}278/** epoch_check handles the changing of epochs for elements stored in the279* cache. epoch_check should be run before every insert.280*281* First, epoch_check decrements and checks the cheap heuristic, and then does282* a more expensive scan if the cheap heuristic runs out. If the expensive283* scan succeeds, the epochs are aged and old elements are allow_erased. The284* cheap heuristic is reset to retrigger after the worst case growth of the285* current epoch's elements would exceed the epoch_size.286*/287void epoch_check()288{289if (epoch_heuristic_counter != 0) {290--epoch_heuristic_counter;291return;292}293// count the number of elements from the latest epoch which294// have not been erased.295uint32_t epoch_unused_count = 0;296for (uint32_t i = 0; i < size; ++i)297epoch_unused_count += epoch_flags[i] &&298!collection_flags.bit_is_set(i);299// If there are more non-deleted entries in the current epoch than the300// epoch size, then allow_erase on all elements in the old epoch (marked301// false) and move all elements in the current epoch to the old epoch302// but do not call allow_erase on their indices.303if (epoch_unused_count >= epoch_size) {304for (uint32_t i = 0; i < size; ++i)305if (epoch_flags[i])306epoch_flags[i] = false;307else308allow_erase(i);309epoch_heuristic_counter = epoch_size;310} else311// reset the epoch_heuristic_counter to next do a scan when worst312// case behavior (no intermittent erases) would exceed epoch size,313// with a reasonable minimum scan size.314// Ordinarily, we would have to sanity check std::min(epoch_size,315// epoch_unused_count), but we already know that `epoch_unused_count316// < epoch_size` in this branch317epoch_heuristic_counter = std::max(1u, std::max(epoch_size / 16,318epoch_size - epoch_unused_count));319}321public:322/** You must always construct a cache with some elements via a subsequent323* call to setup or setup_bytes, otherwise operations may segfault.324*/325cache() : table(), collection_flags(0), epoch_flags(), hash_function()326{327}329/** setup initializes the container to store no more than new_size330* elements and no less than 2 elements.331*332* setup should only be called once. TestOnlyReset() is the exception.333*334* @param new_size the desired number of elements to store335* @returns the maximum number of elements storable336*/337uint32_t setup(uint32_t new_size)338{339// depth_limit must be at least one otherwise errors can occur.340size = std::max<uint32_t>(2, new_size);341depth_limit = static_cast<uint8_t>(std::log2(static_cast<float>(size)));342table.resize(size);343collection_flags.setup(size);344epoch_flags.resize(size);345// Set to 45% as described above346epoch_size = std::max(uint32_t{1}, (45 * size) / 100);347// Initially set to wait for a whole epoch348epoch_heuristic_counter = epoch_size;349return size;350}352/** setup_bytes is a convenience function which accounts for internal memory353* usage when deciding how many elements to store. It isn't perfect because354* it doesn't account for any overhead (struct size, MallocUsage, collection355* and epoch flags). This was done to simplify selecting a power of two356* size. In the expected use case, an extra two bits per entry should be357* negligible compared to the size of the elements.358*359* @param bytes the approximate number of bytes to use for this data360* structure361* @returns A pair of the maximum number of elements storable (see setup()362* documentation for more detail) and the approximate total size of these363* elements in bytes.364*/365std::pair<uint32_t, size_t> setup_bytes(size_t bytes)366{367uint32_t requested_num_elems(std::min<size_t>(368bytes / sizeof(Element),369std::numeric_limits<uint32_t>::max()));371auto num_elems = setup(requested_num_elems);373size_t approx_size_bytes = num_elems * sizeof(Element);374return std::make_pair(num_elems, approx_size_bytes);375}377/** insert loops at most depth_limit times trying to insert a hash378* at various locations in the table via a variant of the Cuckoo Algorithm379* with eight hash locations.380*381* It drops the last tried element if it runs out of depth before382* encountering an open slot.383*384* Thus:385*386* ```387* insert(x);388* return contains(x, false);389* ```390*391* is not guaranteed to return true.392*393* @param e the element to insert394* @post one of the following: All previously inserted elements and e are395* now in the table, one previously inserted element is evicted from the396* table, the entry attempted to be inserted is evicted.397*/398inline void insert(Element e)399{400epoch_check();401uint32_t last_loc = invalid();402bool last_epoch = true;403std::array<uint32_t, 8> locs = compute_hashes(e);404// Make sure we have not already inserted this element405// If we have, make sure that it does not get deleted406for (const uint32_t loc : locs)407if (table[loc] == e) {408please_keep(loc);409epoch_flags[loc] = last_epoch;410return;411}412for (uint8_t depth = 0; depth < depth_limit; ++depth) {413// First try to insert to an empty slot, if one exists414for (const uint32_t loc : locs) {415if (!collection_flags.bit_is_set(loc))416continue;417table[loc] = std::move(e);418please_keep(loc);419epoch_flags[loc] = last_epoch;420return;421}422/** Swap with the element at the location that was423* not the last one looked at. Example:424*425* 1. On first iteration, last_loc == invalid(), find returns last, so426* last_loc defaults to locs[0].427* 2. On further iterations, where last_loc == locs[k], last_loc will428* go to locs[k+1 % 8], i.e., next of the 8 indices wrapping around429* to 0 if needed.430*431* This prevents moving the element we just put in.432*433* The swap is not a move -- we must switch onto the evicted element434* for the next iteration.435*/436last_loc = locs[(1 + (std::find(locs.begin(), locs.end(), last_loc) - locs.begin())) & 7];437std::swap(table[last_loc], e);438// Can't std::swap a std::vector<bool>::reference and a bool&.439bool epoch = last_epoch;440last_epoch = epoch_flags[last_loc];441epoch_flags[last_loc] = epoch;443// Recompute the locs -- unfortunately happens one too many times!444locs = compute_hashes(e);445}446}448/** contains iterates through the hash locations for a given element449* and checks to see if it is present.450*451* contains does not check garbage collected state (in other words,452* garbage is only collected when the space is needed), so:453*454* ```455* insert(x);456* if (contains(x, true))457* return contains(x, false);458* else459* return true;460* ```461*462* executed on a single thread will always return true!463*464* This is a great property for re-org performance for example.465*466* contains returns a bool set true if the element was found.467*468* @param e the element to check469* @param erase whether to attempt setting the garbage collect flag470*471* @post if erase is true and the element is found, then the garbage collect472* flag is set473* @returns true if the element is found, false otherwise474*/475inline bool contains(const Element& e, const bool erase) const476{477std::array<uint32_t, 8> locs = compute_hashes(e);478for (const uint32_t loc : locs)479if (table[loc] == e) {480if (erase)481allow_erase(loc);482return true;483}484return false;485}487/** Empty the cache and re-run setup(). */488void TestOnlyReset()489{490table.clear();491epoch_flags.clear();492setup(0);493}494};495} // namespace CuckooCache497#endif // BITCOIN_CUCKOOCACHE_H
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.