Coverage Report

Created: 2026-07-14 18:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
450k
    size_t FirstPart() const noexcept { return std::min(m_capacity - m_offset, m_size); }
38
39
    void Reallocate(size_t capacity)
40
1.52M
    {
41
1.52M
        Assume(capacity >= m_size);
42
1.52M
        Assume((m_offset == 0 && m_capacity == 0) || m_offset < m_capacity);
43
        // Allocate new buffer.
44
1.52M
        T* new_buffer = capacity ? std::allocator<T>().allocate(capacity) : nullptr;
  Branch (44:25): [True: 450k, False: 358k]
  Branch (44:25): [True: 358k, False: 358k]
45
1.52M
        if (capacity) {
  Branch (45:13): [True: 450k, False: 358k]
  Branch (45:13): [True: 358k, False: 358k]
46
809k
            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
450k
                size_t first_part = FirstPart();
49
450k
                if (first_part != 0) {
  Branch (49:21): [True: 91.8k, False: 358k]
50
91.8k
                    std::memcpy(new_buffer, m_buffer + m_offset, first_part * sizeof(T));
51
91.8k
                }
52
450k
                if (first_part != m_size) {
  Branch (52:21): [True: 168, False: 450k]
53
168
                    std::memcpy(new_buffer + first_part, m_buffer, (m_size - first_part) * sizeof(T));
54
168
                }
55
450k
            } else {
56
                // Otherwise move-construct in place in the new buffer, and destroy old buffer objects.
57
358k
                size_t old_pos = m_offset;
58
358k
                for (size_t new_pos = 0; new_pos < m_size; ++new_pos) {
  Branch (58:42): [True: 0, False: 358k]
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;
  Branch (62:25): [True: 0, False: 0]
63
0
                }
64
358k
            }
65
809k
        }
66
        // Deallocate old buffer and update housekeeping.
67
1.52M
        std::allocator<T>().deallocate(m_buffer, m_capacity);
68
1.52M
        m_buffer = new_buffer;
69
1.52M
        m_offset = 0;
70
1.52M
        m_capacity = capacity;
71
1.52M
        Assume((m_offset == 0 && m_capacity == 0) || m_offset < m_capacity);
72
1.52M
    }
VecDeque<unsigned char>::Reallocate(unsigned long)
Line
Count
Source
40
809k
    {
41
809k
        Assume(capacity >= m_size);
42
809k
        Assume((m_offset == 0 && m_capacity == 0) || m_offset < m_capacity);
43
        // Allocate new buffer.
44
809k
        T* new_buffer = capacity ? std::allocator<T>().allocate(capacity) : nullptr;
  Branch (44:25): [True: 450k, False: 358k]
45
809k
        if (capacity) {
  Branch (45:13): [True: 450k, False: 358k]
46
450k
            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
450k
                size_t first_part = FirstPart();
49
450k
                if (first_part != 0) {
  Branch (49:21): [True: 91.8k, False: 358k]
50
91.8k
                    std::memcpy(new_buffer, m_buffer + m_offset, first_part * sizeof(T));
51
91.8k
                }
52
450k
                if (first_part != m_size) {
  Branch (52:21): [True: 168, False: 450k]
53
168
                    std::memcpy(new_buffer + first_part, m_buffer, (m_size - first_part) * sizeof(T));
54
168
                }
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
450k
        }
66
        // Deallocate old buffer and update housekeeping.
67
809k
        std::allocator<T>().deallocate(m_buffer, m_capacity);
68
809k
        m_buffer = new_buffer;
69
809k
        m_offset = 0;
70
809k
        m_capacity = capacity;
71
809k
        Assume((m_offset == 0 && m_capacity == 0) || m_offset < m_capacity);
72
809k
    }
VecDeque<std::tuple<unsigned char, unsigned int, unsigned int> >::Reallocate(unsigned long)
Line
Count
Source
40
717k
    {
41
717k
        Assume(capacity >= m_size);
42
717k
        Assume((m_offset == 0 && m_capacity == 0) || m_offset < m_capacity);
43
        // Allocate new buffer.
44
717k
        T* new_buffer = capacity ? std::allocator<T>().allocate(capacity) : nullptr;
  Branch (44:25): [True: 358k, False: 358k]
45
717k
        if (capacity) {
  Branch (45:13): [True: 358k, False: 358k]
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
358k
            } else {
56
                // Otherwise move-construct in place in the new buffer, and destroy old buffer objects.
57
358k
                size_t old_pos = m_offset;
58
358k
                for (size_t new_pos = 0; new_pos < m_size; ++new_pos) {
  Branch (58:42): [True: 0, False: 358k]
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;
  Branch (62:25): [True: 0, False: 0]
63
0
                }
64
358k
            }
65
358k
        }
66
        // Deallocate old buffer and update housekeeping.
67
717k
        std::allocator<T>().deallocate(m_buffer, m_capacity);
68
717k
        m_buffer = new_buffer;
69
717k
        m_offset = 0;
70
717k
        m_capacity = capacity;
71
717k
        Assume((m_offset == 0 && m_capacity == 0) || m_offset < m_capacity);
72
717k
    }
73
74
    /** What index in the buffer does logical entry number pos have? */
75
    size_t BufferIndex(size_t pos) const noexcept
76
12.7M
    {
77
12.7M
        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
12.7M
        if (pos >= m_capacity - m_offset) {
  Branch (80:13): [True: 147k, False: 2.60M]
  Branch (80:13): [True: 1.31M, False: 8.71M]
81
1.45M
            return (m_offset + pos) - m_capacity;
82
11.3M
        } else {
83
11.3M
            return m_offset + pos;
84
11.3M
        }
85
12.7M
    }
VecDeque<unsigned char>::BufferIndex(unsigned long) const
Line
Count
Source
76
2.74M
    {
77
2.74M
        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
2.74M
        if (pos >= m_capacity - m_offset) {
  Branch (80:13): [True: 147k, False: 2.60M]
81
147k
            return (m_offset + pos) - m_capacity;
82
2.60M
        } else {
83
2.60M
            return m_offset + pos;
84
2.60M
        }
85
2.74M
    }
VecDeque<std::tuple<unsigned char, unsigned int, unsigned int> >::BufferIndex(unsigned long) const
Line
Count
Source
76
10.0M
    {
77
10.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
10.0M
        if (pos >= m_capacity - m_offset) {
  Branch (80:13): [True: 1.31M, False: 8.71M]
81
1.31M
            return (m_offset + pos) - m_capacity;
82
8.71M
        } else {
83
8.71M
            return m_offset + pos;
84
8.71M
        }
85
10.0M
    }
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
1.07M
    {
91
1.07M
        Assume(size <= m_size);
92
1.07M
        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
1.07M
            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
1.07M
    }
VecDeque<std::tuple<unsigned char, unsigned int, unsigned int> >::ResizeDown(unsigned long)
Line
Count
Source
90
717k
    {
91
717k
        Assume(size <= m_size);
92
717k
        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
717k
            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
717k
    }
VecDeque<unsigned char>::ResizeDown(unsigned long)
Line
Count
Source
90
358k
    {
91
358k
        Assume(size <= m_size);
92
358k
        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
358k
            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
358k
    }
105
106
public:
107
717k
    VecDeque() noexcept = default;
VecDeque<unsigned char>::VecDeque()
Line
Count
Source
107
358k
    VecDeque() noexcept = default;
VecDeque<std::tuple<unsigned char, unsigned int, unsigned int> >::VecDeque()
Line
Count
Source
107
358k
    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
1.07M
    void clear() noexcept { ResizeDown(0); }
VecDeque<std::tuple<unsigned char, unsigned int, unsigned int> >::clear()
Line
Count
Source
126
717k
    void clear() noexcept { ResizeDown(0); }
VecDeque<unsigned char>::clear()
Line
Count
Source
126
358k
    void clear() noexcept { ResizeDown(0); }
127
128
    /** Destroy a deque. */
129
    ~VecDeque()
130
717k
    {
131
717k
        clear();
132
717k
        Reallocate(0);
133
717k
    }
VecDeque<std::tuple<unsigned char, unsigned int, unsigned int> >::~VecDeque()
Line
Count
Source
130
358k
    {
131
358k
        clear();
132
358k
        Reallocate(0);
133
358k
    }
VecDeque<unsigned char>::~VecDeque()
Line
Count
Source
130
358k
    {
131
358k
        clear();
132
358k
        Reallocate(0);
133
358k
    }
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
358k
    {
208
358k
        if (capacity > m_capacity) Reallocate(capacity);
  Branch (208:13): [True: 358k, False: 0]
209
358k
    }
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
8.56M
    {
221
8.56M
        if (m_size == m_capacity) Reallocate((m_size + 1) * 2);
  Branch (221:13): [True: 447k, False: 386k]
  Branch (221:13): [True: 197, False: 3.05k]
  Branch (221:13): [True: 3.21k, False: 836k]
  Branch (221:13): [True: 0, False: 840k]
  Branch (221:13): [True: 0, False: 3.44M]
  Branch (221:13): [True: 0, False: 2.60M]
222
8.56M
        std::construct_at(m_buffer + BufferIndex(m_size), std::forward<Args>(args)...);
223
8.56M
        ++m_size;
224
8.56M
    }
void VecDeque<unsigned char>::emplace_back<unsigned int&>(unsigned int&)
Line
Count
Source
220
834k
    {
221
834k
        if (m_size == m_capacity) Reallocate((m_size + 1) * 2);
  Branch (221:13): [True: 447k, False: 386k]
222
834k
        std::construct_at(m_buffer + BufferIndex(m_size), std::forward<Args>(args)...);
223
834k
        ++m_size;
224
834k
    }
void VecDeque<unsigned char>::emplace_back<unsigned char const&>(unsigned char const&)
Line
Count
Source
220
3.24k
    {
221
3.24k
        if (m_size == m_capacity) Reallocate((m_size + 1) * 2);
  Branch (221:13): [True: 197, False: 3.05k]
222
3.24k
        std::construct_at(m_buffer + BufferIndex(m_size), std::forward<Args>(args)...);
223
3.24k
        ++m_size;
224
3.24k
    }
void VecDeque<unsigned char>::emplace_back<unsigned char>(unsigned char&&)
Line
Count
Source
220
839k
    {
221
839k
        if (m_size == m_capacity) Reallocate((m_size + 1) * 2);
  Branch (221:13): [True: 3.21k, False: 836k]
222
839k
        std::construct_at(m_buffer + BufferIndex(m_size), std::forward<Args>(args)...);
223
839k
        ++m_size;
224
839k
    }
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
840k
    {
221
840k
        if (m_size == m_capacity) Reallocate((m_size + 1) * 2);
  Branch (221:13): [True: 0, False: 840k]
222
840k
        std::construct_at(m_buffer + BufferIndex(m_size), std::forward<Args>(args)...);
223
840k
        ++m_size;
224
840k
    }
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
3.44M
    {
221
3.44M
        if (m_size == m_capacity) Reallocate((m_size + 1) * 2);
  Branch (221:13): [True: 0, False: 3.44M]
222
3.44M
        std::construct_at(m_buffer + BufferIndex(m_size), std::forward<Args>(args)...);
223
3.44M
        ++m_size;
224
3.44M
    }
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
2.60M
    {
221
2.60M
        if (m_size == m_capacity) Reallocate((m_size + 1) * 2);
  Branch (221:13): [True: 0, False: 2.60M]
222
2.60M
        std::construct_at(m_buffer + BufferIndex(m_size), std::forward<Args>(args)...);
223
2.60M
        ++m_size;
224
2.60M
    }
225
226
    /** Move-construct a new element at the end of the deque. */
227
839k
    void push_back(T&& elem) { emplace_back(std::move(elem)); }
228
229
    /** Copy-construct a new element at the end of the deque. */
230
3.24k
    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
8.56M
    {
252
8.56M
        Assume(m_size);
253
8.56M
        std::destroy_at(m_buffer + m_offset);
254
8.56M
        --m_size;
255
8.56M
        ++m_offset;
256
8.56M
        if (m_offset == m_capacity) m_offset = 0;
  Branch (256:13): [True: 608k, False: 1.06M]
  Branch (256:13): [True: 449k, False: 6.43M]
257
8.56M
    }
VecDeque<unsigned char>::pop_front()
Line
Count
Source
251
1.67M
    {
252
1.67M
        Assume(m_size);
253
1.67M
        std::destroy_at(m_buffer + m_offset);
254
1.67M
        --m_size;
255
1.67M
        ++m_offset;
256
1.67M
        if (m_offset == m_capacity) m_offset = 0;
  Branch (256:13): [True: 608k, False: 1.06M]
257
1.67M
    }
VecDeque<std::tuple<unsigned char, unsigned int, unsigned int> >::pop_front()
Line
Count
Source
251
6.88M
    {
252
6.88M
        Assume(m_size);
253
6.88M
        std::destroy_at(m_buffer + m_offset);
254
6.88M
        --m_size;
255
6.88M
        ++m_offset;
256
6.88M
        if (m_offset == m_capacity) m_offset = 0;
  Branch (256:13): [True: 449k, False: 6.43M]
257
6.88M
    }
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
8.56M
    {
270
8.56M
        Assume(m_size);
271
8.56M
        return m_buffer[m_offset];
272
8.56M
    }
VecDeque<unsigned char>::front()
Line
Count
Source
269
1.67M
    {
270
1.67M
        Assume(m_size);
271
1.67M
        return m_buffer[m_offset];
272
1.67M
    }
VecDeque<std::tuple<unsigned char, unsigned int, unsigned int> >::front()
Line
Count
Source
269
6.88M
    {
270
6.88M
        Assume(m_size);
271
6.88M
        return m_buffer[m_offset];
272
6.88M
    }
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
2.10M
    {
284
2.10M
        Assume(m_size);
285
2.10M
        return m_buffer[BufferIndex(m_size - 1)];
286
2.10M
    }
VecDeque<unsigned char>::back()
Line
Count
Source
283
536k
    {
284
536k
        Assume(m_size);
285
536k
        return m_buffer[BufferIndex(m_size - 1)];
286
536k
    }
VecDeque<std::tuple<unsigned char, unsigned int, unsigned int> >::back()
Line
Count
Source
283
1.57M
    {
284
1.57M
        Assume(m_size);
285
1.57M
        return m_buffer[BufferIndex(m_size - 1)];
286
1.57M
    }
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
2.10M
    {
298
2.10M
        Assume(idx < m_size);
299
2.10M
        return m_buffer[BufferIndex(idx)];
300
2.10M
    }
VecDeque<unsigned char>::operator[](unsigned long)
Line
Count
Source
297
536k
    {
298
536k
        Assume(idx < m_size);
299
536k
        return m_buffer[BufferIndex(idx)];
300
536k
    }
VecDeque<std::tuple<unsigned char, unsigned int, unsigned int> >::operator[](unsigned long)
Line
Count
Source
297
1.57M
    {
298
1.57M
        Assume(idx < m_size);
299
1.57M
        return m_buffer[BufferIndex(idx)];
300
1.57M
    }
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
10.8M
    bool empty() const noexcept { return m_size == 0; }
VecDeque<unsigned char>::empty() const
Line
Count
Source
310
3.58M
    bool empty() const noexcept { return m_size == 0; }
VecDeque<std::tuple<unsigned char, unsigned int, unsigned int> >::empty() const
Line
Count
Source
310
7.24M
    bool empty() const noexcept { return m_size == 0; }
311
    /** Get the number of elements in this deque. */
312
7.04M
    size_t size() const noexcept { return m_size; }
VecDeque<unsigned char>::size() const
Line
Count
Source
312
3.70M
    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