/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 |