Coverage Report

Created: 2026-04-29 19:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/tmp/bitcoin/src/util/vecdeque.h
Line
Count
Source
1
// Copyright (c) The Bitcoin Core developers
2
// Distributed under the MIT software license, see the accompanying
3
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5
#ifndef BITCOIN_UTIL_VECDEQUE_H
6
#define BITCOIN_UTIL_VECDEQUE_H
7
8
#include <util/check.h>
9
10
#include <cstring>
11
#include <memory>
12
#include <type_traits>
13
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
 */
23
template<typename T>
24
class VecDeque
25
{
26
    /** Pointer to allocated memory. Can contain constructed and uninitialized T objects. */
27
    T* 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. */
30
    size_t m_offset{0};
31
    /** Number of objects in the container. 0 <= m_size <= m_capacity. */
32
    size_t m_size{0};
33
    /** The size of m_buffer, expressed as a multiple of the size of T. */
34
    size_t m_capacity{0};
35
36
    /** Returns the number of populated objects between m_offset and the end of the buffer. */
37
480k
    size_t FirstPart() const noexcept { return std::min(m_capacity - m_offset, m_size); }
38
39
    void Reallocate(size_t capacity)
40
1.05M
    {
41
1.05M
        Assume(capacity >= m_size);
42
1.05M
        Assume((m_offset == 0 && m_capacity == 0) || m_offset < m_capacity);
43
        // Allocate new buffer.
44
1.05M
        T* new_buffer = capacity ? std::allocator<T>().allocate(capacity) : nullptr;
45
1.05M
        if (capacity) {
46
671k
            if constexpr (std::is_trivially_copyable_v<T>) {
47
                // When T is trivially copyable, just copy the data over from old to new buffer.
48
480k
                size_t first_part = FirstPart();
49
480k
                if (first_part != 0) {
50
288k
                    std::memcpy(new_buffer, m_buffer + m_offset, first_part * sizeof(T));
51
288k
                }
52
480k
                if (first_part != m_size) {
53
1.21k
                    std::memcpy(new_buffer + first_part, m_buffer, (m_size - first_part) * sizeof(T));
54
1.21k
                }
55
480k
            } else {
56
                // Otherwise move-construct in place in the new buffer, and destroy old buffer objects.
57
191k
                size_t old_pos = m_offset;
58
191k
                for (size_t new_pos = 0; new_pos < m_size; ++new_pos) {
59
0
                    std::construct_at(new_buffer + new_pos, std::move(*(m_buffer + old_pos)));
60
0
                    std::destroy_at(m_buffer + old_pos);
61
0
                    ++old_pos;
62
0
                    if (old_pos == m_capacity) old_pos = 0;
63
0
                }
64
191k
            }
65
671k
        }
66
        // Deallocate old buffer and update housekeeping.
67
1.05M
        std::allocator<T>().deallocate(m_buffer, m_capacity);
68
1.05M
        m_buffer = new_buffer;
69
1.05M
        m_offset = 0;
70
1.05M
        m_capacity = capacity;
71
1.05M
        Assume((m_offset == 0 && m_capacity == 0) || m_offset < m_capacity);
72
1.05M
    }
VecDeque<unsigned char>::Reallocate(unsigned long)
Line
Count
Source
40
671k
    {
41
671k
        Assume(capacity >= m_size);
42
671k
        Assume((m_offset == 0 && m_capacity == 0) || m_offset < m_capacity);
43
        // Allocate new buffer.
44
671k
        T* new_buffer = capacity ? std::allocator<T>().allocate(capacity) : nullptr;
45
671k
        if (capacity) {
46
480k
            if constexpr (std::is_trivially_copyable_v<T>) {
47
                // When T is trivially copyable, just copy the data over from old to new buffer.
48
480k
                size_t first_part = FirstPart();
49
480k
                if (first_part != 0) {
50
288k
                    std::memcpy(new_buffer, m_buffer + m_offset, first_part * sizeof(T));
51
288k
                }
52
480k
                if (first_part != m_size) {
53
1.21k
                    std::memcpy(new_buffer + first_part, m_buffer, (m_size - first_part) * sizeof(T));
54
1.21k
                }
55
            } else {
56
                // Otherwise move-construct in place in the new buffer, and destroy old buffer objects.
57
                size_t old_pos = m_offset;
58
                for (size_t new_pos = 0; new_pos < m_size; ++new_pos) {
59
                    std::construct_at(new_buffer + new_pos, std::move(*(m_buffer + old_pos)));
60
                    std::destroy_at(m_buffer + old_pos);
61
                    ++old_pos;
62
                    if (old_pos == m_capacity) old_pos = 0;
63
                }
64
            }
65
480k
        }
66
        // Deallocate old buffer and update housekeeping.
67
671k
        std::allocator<T>().deallocate(m_buffer, m_capacity);
68
671k
        m_buffer = new_buffer;
69
671k
        m_offset = 0;
70
671k
        m_capacity = capacity;
71
671k
        Assume((m_offset == 0 && m_capacity == 0) || m_offset < m_capacity);
72
671k
    }
VecDeque<std::tuple<unsigned char, unsigned int, unsigned int>>::Reallocate(unsigned long)
Line
Count
Source
40
382k
    {
41
382k
        Assume(capacity >= m_size);
42
382k
        Assume((m_offset == 0 && m_capacity == 0) || m_offset < m_capacity);
43
        // Allocate new buffer.
44
382k
        T* new_buffer = capacity ? std::allocator<T>().allocate(capacity) : nullptr;
45
382k
        if (capacity) {
46
            if constexpr (std::is_trivially_copyable_v<T>) {
47
                // When T is trivially copyable, just copy the data over from old to new buffer.
48
                size_t first_part = FirstPart();
49
                if (first_part != 0) {
50
                    std::memcpy(new_buffer, m_buffer + m_offset, first_part * sizeof(T));
51
                }
52
                if (first_part != m_size) {
53
                    std::memcpy(new_buffer + first_part, m_buffer, (m_size - first_part) * sizeof(T));
54
                }
55
191k
            } else {
56
                // Otherwise move-construct in place in the new buffer, and destroy old buffer objects.
57
191k
                size_t old_pos = m_offset;
58
191k
                for (size_t new_pos = 0; new_pos < m_size; ++new_pos) {
59
0
                    std::construct_at(new_buffer + new_pos, std::move(*(m_buffer + old_pos)));
60
0
                    std::destroy_at(m_buffer + old_pos);
61
0
                    ++old_pos;
62
0
                    if (old_pos == m_capacity) old_pos = 0;
63
0
                }
64
191k
            }
65
191k
        }
66
        // Deallocate old buffer and update housekeeping.
67
382k
        std::allocator<T>().deallocate(m_buffer, m_capacity);
68
382k
        m_buffer = new_buffer;
69
382k
        m_offset = 0;
70
382k
        m_capacity = capacity;
71
382k
        Assume((m_offset == 0 && m_capacity == 0) || m_offset < m_capacity);
72
382k
    }
73
74
    /** What index in the buffer does logical entry number pos have? */
75
    size_t BufferIndex(size_t pos) const noexcept
76
14.0M
    {
77
14.0M
        Assume(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.
80
14.0M
        if (pos >= m_capacity - m_offset) {
81
823k
            return (m_offset + pos) - m_capacity;
82
13.2M
        } else {
83
13.2M
            return m_offset + pos;
84
13.2M
        }
85
14.0M
    }
VecDeque<unsigned char>::BufferIndex(unsigned long) const
Line
Count
Source
76
9.46M
    {
77
9.46M
        Assume(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.
80
9.46M
        if (pos >= m_capacity - m_offset) {
81
750k
            return (m_offset + pos) - m_capacity;
82
8.71M
        } else {
83
8.71M
            return m_offset + pos;
84
8.71M
        }
85
9.46M
    }
VecDeque<std::tuple<unsigned char, unsigned int, unsigned int>>::BufferIndex(unsigned long) const
Line
Count
Source
76
4.62M
    {
77
4.62M
        Assume(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.
80
4.62M
        if (pos >= m_capacity - m_offset) {
81
73.0k
            return (m_offset + pos) - m_capacity;
82
4.55M
        } else {
83
4.55M
            return m_offset + pos;
84
4.55M
        }
85
4.62M
    }
86
87
    /** Specialization of resize() that can only shrink. Separate so that clear() can call it
88
     *  without requiring a default T constructor. */
89
    void ResizeDown(size_t size) noexcept
90
574k
    {
91
574k
        Assume(size <= m_size);
92
574k
        if constexpr (std::is_trivially_destructible_v<T>) {
93
            // If T is trivially destructible, we do not need to do anything but update the
94
            // housekeeping record. Default constructor or zero-filling will be used when
95
            // the space is reused.
96
574k
            m_size = size;
97
        } else {
98
            // If not, we need to invoke the destructor for every element separately.
99
            while (m_size > size) {
100
                std::destroy_at(m_buffer + BufferIndex(m_size - 1));
101
                --m_size;
102
            }
103
        }
104
574k
    }
VecDeque<std::tuple<unsigned char, unsigned int, unsigned int>>::ResizeDown(unsigned long)
Line
Count
Source
90
382k
    {
91
382k
        Assume(size <= m_size);
92
382k
        if constexpr (std::is_trivially_destructible_v<T>) {
93
            // If T is trivially destructible, we do not need to do anything but update the
94
            // housekeeping record. Default constructor or zero-filling will be used when
95
            // the space is reused.
96
382k
            m_size = size;
97
        } else {
98
            // If not, we need to invoke the destructor for every element separately.
99
            while (m_size > size) {
100
                std::destroy_at(m_buffer + BufferIndex(m_size - 1));
101
                --m_size;
102
            }
103
        }
104
382k
    }
VecDeque<unsigned char>::ResizeDown(unsigned long)
Line
Count
Source
90
191k
    {
91
191k
        Assume(size <= m_size);
92
191k
        if constexpr (std::is_trivially_destructible_v<T>) {
93
            // If T is trivially destructible, we do not need to do anything but update the
94
            // housekeeping record. Default constructor or zero-filling will be used when
95
            // the space is reused.
96
191k
            m_size = size;
97
        } else {
98
            // If not, we need to invoke the destructor for every element separately.
99
            while (m_size > size) {
100
                std::destroy_at(m_buffer + BufferIndex(m_size - 1));
101
                --m_size;
102
            }
103
        }
104
191k
    }
105
106
public:
107
382k
    VecDeque() noexcept = default;
VecDeque<unsigned char>::VecDeque()
Line
Count
Source
107
191k
    VecDeque() noexcept = default;
VecDeque<std::tuple<unsigned char, unsigned int, unsigned int>>::VecDeque()
Line
Count
Source
107
191k
    VecDeque() noexcept = default;
108
109
    /** Resize the deque to be exactly size size (adding default-constructed elements if needed). */
110
    void resize(size_t size)
111
    {
112
        if (size < m_size) {
113
            // Delegate to ResizeDown when shrinking.
114
            ResizeDown(size);
115
        } else if (size > m_size) {
116
            // When growing, first see if we need to allocate more space.
117
            if (size > m_capacity) Reallocate(size);
118
            while (m_size < size) {
119
                std::construct_at(m_buffer + BufferIndex(m_size));
120
                ++m_size;
121
            }
122
        }
123
    }
124
125
    /** Resize the deque to be size 0. The capacity will remain unchanged. */
126
574k
    void clear() noexcept { ResizeDown(0); }
VecDeque<std::tuple<unsigned char, unsigned int, unsigned int>>::clear()
Line
Count
Source
126
382k
    void clear() noexcept { ResizeDown(0); }
VecDeque<unsigned char>::clear()
Line
Count
Source
126
191k
    void clear() noexcept { ResizeDown(0); }
127
128
    /** Destroy a deque. */
129
    ~VecDeque()
130
382k
    {
131
382k
        clear();
132
382k
        Reallocate(0);
133
382k
    }
VecDeque<std::tuple<unsigned char, unsigned int, unsigned int>>::~VecDeque()
Line
Count
Source
130
191k
    {
131
191k
        clear();
132
191k
        Reallocate(0);
133
191k
    }
VecDeque<unsigned char>::~VecDeque()
Line
Count
Source
130
191k
    {
131
191k
        clear();
132
191k
        Reallocate(0);
133
191k
    }
134
135
    /** Copy-assign a deque. */
136
    VecDeque& operator=(const VecDeque& other)
137
    {
138
        if (&other == this) [[unlikely]] return *this;
139
        clear();
140
        Reallocate(other.m_size);
141
        if constexpr (std::is_trivially_copyable_v<T>) {
142
            size_t first_part = other.FirstPart();
143
            Assume(first_part > 0 || m_size == 0);
144
            if (first_part != 0) {
145
                std::memcpy(m_buffer, other.m_buffer + other.m_offset, first_part * sizeof(T));
146
            }
147
            if (first_part != other.m_size) {
148
                std::memcpy(m_buffer + first_part, other.m_buffer, (other.m_size - first_part) * sizeof(T));
149
            }
150
            m_size = other.m_size;
151
        } else {
152
            while (m_size < other.m_size) {
153
                std::construct_at(m_buffer + BufferIndex(m_size), other[m_size]);
154
                ++m_size;
155
            }
156
        }
157
        return *this;
158
    }
159
160
    /** Swap two deques. */
161
    void swap(VecDeque& other) noexcept
162
    {
163
        std::swap(m_buffer, other.m_buffer);
164
        std::swap(m_offset, other.m_offset);
165
        std::swap(m_size, other.m_size);
166
        std::swap(m_capacity, other.m_capacity);
167
    }
168
169
    /** Non-member version of swap. */
170
    friend void swap(VecDeque& a, VecDeque& b) noexcept { a.swap(b); }
171
172
    /** Move-assign a deque. */
173
    VecDeque& operator=(VecDeque&& other) noexcept
174
    {
175
        swap(other);
176
        return *this;
177
    }
178
179
    /** Copy-construct a deque. */
180
    VecDeque(const VecDeque& other) { *this = other; }
181
    /** Move-construct a deque. */
182
    VecDeque(VecDeque&& other) noexcept { swap(other); }
183
184
    /** Equality comparison between two deques (only compares size+contents, not capacity). */
185
    bool friend operator==(const VecDeque& a, const VecDeque& b)
186
    {
187
        if (a.m_size != b.m_size) return false;
188
        for (size_t i = 0; i < a.m_size; ++i) {
189
            if (a[i] != b[i]) return false;
190
        }
191
        return true;
192
    }
193
194
    /** Comparison between two deques, implementing lexicographic ordering on the contents. */
195
    std::strong_ordering friend operator<=>(const VecDeque& a, const VecDeque& b)
196
    {
197
        size_t pos_a{0}, pos_b{0};
198
        while (pos_a < a.m_size && pos_b < b.m_size) {
199
            auto cmp = a[pos_a++] <=> b[pos_b++];
200
            if (cmp != 0) return cmp;
201
        }
202
        return a.m_size <=> b.m_size;
203
    }
204
205
    /** Increase the capacity to capacity. Capacity will not shrink. */
206
    void reserve(size_t capacity)
207
191k
    {
208
191k
        if (capacity > m_capacity) Reallocate(capacity);
209
191k
    }
210
211
    /** Make the capacity equal to the size. The contents does not change. */
212
    void shrink_to_fit()
213
    {
214
        if (m_capacity > m_size) Reallocate(m_size);
215
    }
216
217
    /** Construct a new element at the end of the deque. */
218
    template<typename... Args>
219
    void emplace_back(Args&&... args)
220
7.02M
    {
221
7.02M
        if (m_size == m_capacity) Reallocate((m_size + 1) * 2);
222
7.02M
        std::construct_at(m_buffer + BufferIndex(m_size), std::forward<Args>(args)...);
223
7.02M
        ++m_size;
224
7.02M
    }
void VecDeque<unsigned char>::emplace_back<unsigned int&>(unsigned int&)
Line
Count
Source
220
1.61M
    {
221
1.61M
        if (m_size == m_capacity) Reallocate((m_size + 1) * 2);
222
1.61M
        std::construct_at(m_buffer + BufferIndex(m_size), std::forward<Args>(args)...);
223
1.61M
        ++m_size;
224
1.61M
    }
void VecDeque<unsigned char>::emplace_back<unsigned char const&>(unsigned char const&)
Line
Count
Source
220
1.94M
    {
221
1.94M
        if (m_size == m_capacity) Reallocate((m_size + 1) * 2);
222
1.94M
        std::construct_at(m_buffer + BufferIndex(m_size), std::forward<Args>(args)...);
223
1.94M
        ++m_size;
224
1.94M
    }
void VecDeque<unsigned char>::emplace_back<unsigned char>(unsigned char&&)
Line
Count
Source
220
1.33M
    {
221
1.33M
        if (m_size == m_capacity) Reallocate((m_size + 1) * 2);
222
1.33M
        std::construct_at(m_buffer + BufferIndex(m_size), std::forward<Args>(args)...);
223
1.33M
        ++m_size;
224
1.33M
    }
void VecDeque<std::tuple<unsigned char, unsigned int, unsigned int>>::emplace_back<unsigned int&, unsigned int&, unsigned long>(unsigned int&, unsigned int&, unsigned long&&)
Line
Count
Source
220
1.50M
    {
221
1.50M
        if (m_size == m_capacity) Reallocate((m_size + 1) * 2);
222
1.50M
        std::construct_at(m_buffer + BufferIndex(m_size), std::forward<Args>(args)...);
223
1.50M
        ++m_size;
224
1.50M
    }
void VecDeque<std::tuple<unsigned char, unsigned int, unsigned int>>::emplace_back<unsigned char&, unsigned int&, unsigned int&>(unsigned char&, unsigned int&, unsigned int&)
Line
Count
Source
220
338k
    {
221
338k
        if (m_size == m_capacity) Reallocate((m_size + 1) * 2);
222
338k
        std::construct_at(m_buffer + BufferIndex(m_size), std::forward<Args>(args)...);
223
338k
        ++m_size;
224
338k
    }
void VecDeque<std::tuple<unsigned char, unsigned int, unsigned int>>::emplace_back<unsigned char&, unsigned int&, unsigned long>(unsigned char&, unsigned int&, unsigned long&&)
Line
Count
Source
220
286k
    {
221
286k
        if (m_size == m_capacity) Reallocate((m_size + 1) * 2);
222
286k
        std::construct_at(m_buffer + BufferIndex(m_size), std::forward<Args>(args)...);
223
286k
        ++m_size;
224
286k
    }
225
226
    /** Move-construct a new element at the end of the deque. */
227
1.33M
    void push_back(T&& elem) { emplace_back(std::move(elem)); }
228
229
    /** Copy-construct a new element at the end of the deque. */
230
1.94M
    void push_back(const T& elem) { emplace_back(elem); }
231
232
    /** Construct a new element at the beginning of the deque. */
233
    template<typename... Args>
234
    void emplace_front(Args&&... args)
235
    {
236
        if (m_size == m_capacity) Reallocate((m_size + 1) * 2);
237
        std::construct_at(m_buffer + BufferIndex(m_capacity - 1), std::forward<Args>(args)...);
238
        if (m_offset == 0) m_offset = m_capacity;
239
        --m_offset;
240
        ++m_size;
241
    }
242
243
    /** Copy-construct a new element at the beginning of the deque. */
244
    void push_front(const T& elem) { emplace_front(elem); }
245
246
    /** Move-construct a new element at the beginning of the deque. */
247
    void push_front(T&& elem) { emplace_front(std::move(elem)); }
248
249
    /** Remove the first element of the deque. Requires !empty(). */
250
    void pop_front()
251
7.02M
    {
252
7.02M
        Assume(m_size);
253
7.02M
        std::destroy_at(m_buffer + m_offset);
254
7.02M
        --m_size;
255
7.02M
        ++m_offset;
256
7.02M
        if (m_offset == m_capacity) m_offset = 0;
257
7.02M
    }
VecDeque<unsigned char>::pop_front()
Line
Count
Source
251
4.89M
    {
252
4.89M
        Assume(m_size);
253
4.89M
        std::destroy_at(m_buffer + m_offset);
254
4.89M
        --m_size;
255
4.89M
        ++m_offset;
256
4.89M
        if (m_offset == m_capacity) m_offset = 0;
257
4.89M
    }
VecDeque<std::tuple<unsigned char, unsigned int, unsigned int>>::pop_front()
Line
Count
Source
251
2.12M
    {
252
2.12M
        Assume(m_size);
253
2.12M
        std::destroy_at(m_buffer + m_offset);
254
2.12M
        --m_size;
255
2.12M
        ++m_offset;
256
2.12M
        if (m_offset == m_capacity) m_offset = 0;
257
2.12M
    }
258
259
    /** Remove the last element of the deque. Requires !empty(). */
260
    void pop_back()
261
    {
262
        Assume(m_size);
263
        std::destroy_at(m_buffer + BufferIndex(m_size - 1));
264
        --m_size;
265
    }
266
267
    /** Get a mutable reference to the first element of the deque. Requires !empty(). */
268
    T& front() noexcept
269
7.02M
    {
270
7.02M
        Assume(m_size);
271
7.02M
        return m_buffer[m_offset];
272
7.02M
    }
VecDeque<unsigned char>::front()
Line
Count
Source
269
4.89M
    {
270
4.89M
        Assume(m_size);
271
4.89M
        return m_buffer[m_offset];
272
4.89M
    }
VecDeque<std::tuple<unsigned char, unsigned int, unsigned int>>::front()
Line
Count
Source
269
2.12M
    {
270
2.12M
        Assume(m_size);
271
2.12M
        return m_buffer[m_offset];
272
2.12M
    }
273
274
    /** Get a const reference to the first element of the deque. Requires !empty(). */
275
    const T& front() const noexcept
276
    {
277
        Assume(m_size);
278
        return m_buffer[m_offset];
279
    }
280
281
    /** Get a mutable reference to the last element of the deque. Requires !empty(). */
282
    T& back() noexcept
283
3.53M
    {
284
3.53M
        Assume(m_size);
285
3.53M
        return m_buffer[BufferIndex(m_size - 1)];
286
3.53M
    }
VecDeque<unsigned char>::back()
Line
Count
Source
283
2.28M
    {
284
2.28M
        Assume(m_size);
285
2.28M
        return m_buffer[BufferIndex(m_size - 1)];
286
2.28M
    }
VecDeque<std::tuple<unsigned char, unsigned int, unsigned int>>::back()
Line
Count
Source
283
1.24M
    {
284
1.24M
        Assume(m_size);
285
1.24M
        return m_buffer[BufferIndex(m_size - 1)];
286
1.24M
    }
287
288
    /** Get a const reference to the last element of the deque. Requires !empty(). */
289
    const T& back() const noexcept
290
    {
291
        Assume(m_size);
292
        return m_buffer[BufferIndex(m_size - 1)];
293
    }
294
295
    /** Get a mutable reference to the element in the deque at the given index. Requires idx < size(). */
296
    T& operator[](size_t idx) noexcept
297
3.53M
    {
298
3.53M
        Assume(idx < m_size);
299
3.53M
        return m_buffer[BufferIndex(idx)];
300
3.53M
    }
VecDeque<unsigned char>::operator[](unsigned long)
Line
Count
Source
297
2.28M
    {
298
2.28M
        Assume(idx < m_size);
299
2.28M
        return m_buffer[BufferIndex(idx)];
300
2.28M
    }
VecDeque<std::tuple<unsigned char, unsigned int, unsigned int>>::operator[](unsigned long)
Line
Count
Source
297
1.24M
    {
298
1.24M
        Assume(idx < m_size);
299
1.24M
        return m_buffer[BufferIndex(idx)];
300
1.24M
    }
301
302
    /** Get a const reference to the element in the deque at the given index. Requires idx < size(). */
303
    const T& operator[](size_t idx) const noexcept
304
    {
305
        Assume(idx < m_size);
306
        return m_buffer[BufferIndex(idx)];
307
    }
308
309
    /** Test whether the contents of this deque is empty. */
310
9.14M
    bool empty() const noexcept { return m_size == 0; }
VecDeque<unsigned char>::empty() const
Line
Count
Source
310
6.81M
    bool empty() const noexcept { return m_size == 0; }
VecDeque<std::tuple<unsigned char, unsigned int, unsigned int>>::empty() const
Line
Count
Source
310
2.32M
    bool empty() const noexcept { return m_size == 0; }
311
    /** Get the number of elements in this deque. */
312
9.42M
    size_t size() const noexcept { return m_size; }
VecDeque<unsigned char>::size() const
Line
Count
Source
312
6.08M
    size_t size() const noexcept { return m_size; }
VecDeque<std::tuple<unsigned char, unsigned int, unsigned int>>::size() const
Line
Count
Source
312
3.34M
    size_t size() const noexcept { return m_size; }
313
    /** Get the capacity of this deque (maximum size it can have without reallocating). */
314
    size_t capacity() const noexcept { return m_capacity; }
315
};
316
317
#endif // BITCOIN_UTIL_VECDEQUE_H