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) 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#ifndef BITCOIN_UTIL_VECDEQUE_H6#define BITCOIN_UTIL_VECDEQUE_H8#include <util/check.h>10#include <cstring>11#include <memory>12#include <type_traits>14/** Data structure largely mimicking std::deque, but using single preallocated ring buffer.15*16* - More efficient and better memory locality than std::deque.17* - Most operations ({push_,pop_,emplace_,}{front,back}(), operator[], ...) are O(1),18* unless reallocation is needed (in which case they are O(n)).19* - Supports reserve(), capacity(), shrink_to_fit() like vectors.20* - No iterator support.21* - Data is not stored in a single contiguous block, so no data().22*/23template<typename T>24class VecDeque25{26/** Pointer to allocated memory. Can contain constructed and uninitialized T objects. */27T* m_buffer{nullptr};28/** m_buffer + m_offset points to first object in queue. m_offset = 0 if m_capacity is 0;29* otherwise 0 <= m_offset < m_capacity. */30size_t m_offset{0};31/** Number of objects in the container. 0 <= m_size <= m_capacity. */32size_t m_size{0};33/** The size of m_buffer, expressed as a multiple of the size of T. */34size_t m_capacity{0};36/** Returns the number of populated objects between m_offset and the end of the buffer. */37size_t FirstPart() const noexcept { return std::min(m_capacity - m_offset, m_size); }39void Reallocate(size_t capacity)40{41Assume(capacity >= m_size);42Assume((m_offset == 0 && m_capacity == 0) || m_offset < m_capacity);43// Allocate new buffer.44T* new_buffer = capacity ? std::allocator<T>().allocate(capacity) : nullptr;45if (capacity) {46if constexpr (std::is_trivially_copyable_v<T>) {47// When T is trivially copyable, just copy the data over from old to new buffer.48size_t first_part = FirstPart();49if (first_part != 0) {50std::memcpy(new_buffer, m_buffer + m_offset, first_part * sizeof(T));51}52if (first_part != m_size) {53std::memcpy(new_buffer + first_part, m_buffer, (m_size - first_part) * sizeof(T));54}55} else {56// Otherwise move-construct in place in the new buffer, and destroy old buffer objects.57size_t old_pos = m_offset;58for (size_t new_pos = 0; new_pos < m_size; ++new_pos) {59std::construct_at(new_buffer + new_pos, std::move(*(m_buffer + old_pos)));60std::destroy_at(m_buffer + old_pos);61++old_pos;62if (old_pos == m_capacity) old_pos = 0;63}64}65}66// Deallocate old buffer and update housekeeping.67std::allocator<T>().deallocate(m_buffer, m_capacity);68m_buffer = new_buffer;69m_offset = 0;70m_capacity = capacity;71Assume((m_offset == 0 && m_capacity == 0) || m_offset < m_capacity);72}74/** What index in the buffer does logical entry number pos have? */75size_t BufferIndex(size_t pos) const noexcept76{77Assume(pos < m_capacity);78// The expression below is used instead of the more obvious (pos + m_offset >= m_capacity),79// because the addition there could in theory overflow with very large deques.80if (pos >= m_capacity - m_offset) {81return (m_offset + pos) - m_capacity;82} else {83return m_offset + pos;84}85}87/** Specialization of resize() that can only shrink. Separate so that clear() can call it88* without requiring a default T constructor. */89void ResizeDown(size_t size) noexcept90{91Assume(size <= m_size);92if constexpr (std::is_trivially_destructible_v<T>) {93// If T is trivially destructible, we do not need to do anything but update the94// housekeeping record. Default constructor or zero-filling will be used when95// the space is reused.96m_size = size;97} else {98// If not, we need to invoke the destructor for every element separately.99while (m_size > size) {100std::destroy_at(m_buffer + BufferIndex(m_size - 1));101--m_size;102}103}104}106public:107VecDeque() noexcept = default;109/** Resize the deque to be exactly size size (adding default-constructed elements if needed). */110void resize(size_t size)111{112if (size < m_size) {113// Delegate to ResizeDown when shrinking.114ResizeDown(size);115} else if (size > m_size) {116// When growing, first see if we need to allocate more space.117if (size > m_capacity) Reallocate(size);118while (m_size < size) {119std::construct_at(m_buffer + BufferIndex(m_size));120++m_size;121}122}123}125/** Resize the deque to be size 0. The capacity will remain unchanged. */126void clear() noexcept { ResizeDown(0); }128/** Destroy a deque. */129~VecDeque()130{131clear();132Reallocate(0);133}135/** Copy-assign a deque. */136VecDeque& operator=(const VecDeque& other)137{138if (&other == this) [[unlikely]] return *this;139clear();140Reallocate(other.m_size);141if constexpr (std::is_trivially_copyable_v<T>) {142size_t first_part = other.FirstPart();143Assume(first_part > 0 || m_size == 0);144if (first_part != 0) {145std::memcpy(m_buffer, other.m_buffer + other.m_offset, first_part * sizeof(T));146}147if (first_part != other.m_size) {148std::memcpy(m_buffer + first_part, other.m_buffer, (other.m_size - first_part) * sizeof(T));149}150m_size = other.m_size;151} else {152while (m_size < other.m_size) {153std::construct_at(m_buffer + BufferIndex(m_size), other[m_size]);154++m_size;155}156}157return *this;158}160/** Swap two deques. */161void swap(VecDeque& other) noexcept162{163std::swap(m_buffer, other.m_buffer);164std::swap(m_offset, other.m_offset);165std::swap(m_size, other.m_size);166std::swap(m_capacity, other.m_capacity);167}169/** Non-member version of swap. */170friend void swap(VecDeque& a, VecDeque& b) noexcept { a.swap(b); }172/** Move-assign a deque. */173VecDeque& operator=(VecDeque&& other) noexcept174{175swap(other);176return *this;177}179/** Copy-construct a deque. */180VecDeque(const VecDeque& other) { *this = other; }181/** Move-construct a deque. */182VecDeque(VecDeque&& other) noexcept { swap(other); }184/** Equality comparison between two deques (only compares size+contents, not capacity). */185bool friend operator==(const VecDeque& a, const VecDeque& b)186{187if (a.m_size != b.m_size) return false;188for (size_t i = 0; i < a.m_size; ++i) {189if (a[i] != b[i]) return false;190}191return true;192}194/** Comparison between two deques, implementing lexicographic ordering on the contents. */195std::strong_ordering friend operator<=>(const VecDeque& a, const VecDeque& b)196{197size_t pos_a{0}, pos_b{0};198while (pos_a < a.m_size && pos_b < b.m_size) {199auto cmp = a[pos_a++] <=> b[pos_b++];200if (cmp != 0) return cmp;201}202return a.m_size <=> b.m_size;203}205/** Increase the capacity to capacity. Capacity will not shrink. */206void reserve(size_t capacity)207{208if (capacity > m_capacity) Reallocate(capacity);209}211/** Make the capacity equal to the size. The contents does not change. */212void shrink_to_fit()213{214if (m_capacity > m_size) Reallocate(m_size);215}217/** Construct a new element at the end of the deque. */218template<typename... Args>219void emplace_back(Args&&... args)220{221if (m_size == m_capacity) Reallocate((m_size + 1) * 2);222std::construct_at(m_buffer + BufferIndex(m_size), std::forward<Args>(args)...);223++m_size;224}226/** Move-construct a new element at the end of the deque. */227void push_back(T&& elem) { emplace_back(std::move(elem)); }229/** Copy-construct a new element at the end of the deque. */230void push_back(const T& elem) { emplace_back(elem); }232/** Construct a new element at the beginning of the deque. */233template<typename... Args>234void emplace_front(Args&&... args)235{236if (m_size == m_capacity) Reallocate((m_size + 1) * 2);237std::construct_at(m_buffer + BufferIndex(m_capacity - 1), std::forward<Args>(args)...);238if (m_offset == 0) m_offset = m_capacity;239--m_offset;240++m_size;241}243/** Copy-construct a new element at the beginning of the deque. */244void push_front(const T& elem) { emplace_front(elem); }246/** Move-construct a new element at the beginning of the deque. */247void push_front(T&& elem) { emplace_front(std::move(elem)); }249/** Remove the first element of the deque. Requires !empty(). */250void pop_front()
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.