Bitcoin Core  29.1.0
P2P Digital Currency
vecdeque.h
Go to the documentation of this file.
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 
23 template<typename T>
24 class VecDeque
25 {
27  T* m_buffer{nullptr};
30  size_t m_offset{0};
32  size_t m_size{0};
34  size_t m_capacity{0};
35 
37  size_t FirstPart() const noexcept { return std::min(m_capacity - m_offset, m_size); }
38 
39  void Reallocate(size_t capacity)
40  {
42  Assume((m_offset == 0 && m_capacity == 0) || m_offset < m_capacity);
43  // Allocate new buffer.
44  T* new_buffer = capacity ? std::allocator<T>().allocate(capacity) : nullptr;
45  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  } 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  }
66  // Deallocate old buffer and update housekeeping.
67  std::allocator<T>().deallocate(m_buffer, m_capacity);
68  m_buffer = new_buffer;
69  m_offset = 0;
71  Assume((m_offset == 0 && m_capacity == 0) || m_offset < m_capacity);
72  }
73 
75  size_t BufferIndex(size_t pos) const noexcept
76  {
77  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  if (pos >= m_capacity - m_offset) {
81  return (m_offset + pos) - m_capacity;
82  } else {
83  return m_offset + pos;
84  }
85  }
86 
89  void ResizeDown(size_t size) noexcept
90  {
91  Assume(size <= m_size);
92  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  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  }
105 
106 public:
107  VecDeque() noexcept = default;
108 
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 
126  void clear() noexcept { ResizeDown(0); }
127 
130  {
131  clear();
132  Reallocate(0);
133  }
134 
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 
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 
170  friend void swap(VecDeque& a, VecDeque& b) noexcept { a.swap(b); }
171 
173  VecDeque& operator=(VecDeque&& other) noexcept
174  {
175  swap(other);
176  return *this;
177  }
178 
180  VecDeque(const VecDeque& other) { *this = other; }
182  VecDeque(VecDeque&& other) noexcept { swap(other); }
183 
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 
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 
206  void reserve(size_t capacity)
207  {
209  }
210 
213  {
215  }
216 
218  template<typename... Args>
219  void emplace_back(Args&&... args)
220  {
221  if (m_size == m_capacity) Reallocate((m_size + 1) * 2);
222  std::construct_at(m_buffer + BufferIndex(m_size), std::forward<Args>(args)...);
223  ++m_size;
224  }
225 
227  void push_back(T&& elem) { emplace_back(std::move(elem)); }
228 
230  void push_back(const T& elem) { emplace_back(elem); }
231 
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 
244  void push_front(const T& elem) { emplace_front(elem); }
245 
247  void push_front(T&& elem) { emplace_front(std::move(elem)); }
248 
250  void pop_front()
251  {
252  Assume(m_size);
253  std::destroy_at(m_buffer + m_offset);
254  --m_size;
255  ++m_offset;
256  if (m_offset == m_capacity) m_offset = 0;
257  }
258 
260  void pop_back()
261  {
262  Assume(m_size);
263  std::destroy_at(m_buffer + BufferIndex(m_size - 1));
264  --m_size;
265  }
266 
268  T& front() noexcept
269  {
270  Assume(m_size);
271  return m_buffer[m_offset];
272  }
273 
275  const T& front() const noexcept
276  {
277  Assume(m_size);
278  return m_buffer[m_offset];
279  }
280 
282  T& back() noexcept
283  {
284  Assume(m_size);
285  return m_buffer[BufferIndex(m_size - 1)];
286  }
287 
289  const T& back() const noexcept
290  {
291  Assume(m_size);
292  return m_buffer[BufferIndex(m_size - 1)];
293  }
294 
296  T& operator[](size_t idx) noexcept
297  {
298  Assume(idx < m_size);
299  return m_buffer[BufferIndex(idx)];
300  }
301 
303  const T& operator[](size_t idx) const noexcept
304  {
305  Assume(idx < m_size);
306  return m_buffer[BufferIndex(idx)];
307  }
308 
310  bool empty() const noexcept { return m_size == 0; }
312  size_t size() const noexcept { return m_size; }
314  size_t capacity() const noexcept { return m_capacity; }
315 };
316 
317 #endif // BITCOIN_UTIL_VECDEQUE_H
void emplace_front(Args &&... args)
Construct a new element at the beginning of the deque.
Definition: vecdeque.h:234
const T & front() const noexcept
Get a const reference to the first element of the deque.
Definition: vecdeque.h:275
friend void swap(VecDeque &a, VecDeque &b) noexcept
Non-member version of swap.
Definition: vecdeque.h:170
size_t FirstPart() const noexcept
Returns the number of populated objects between m_offset and the end of the buffer.
Definition: vecdeque.h:37
size_t size() const noexcept
Get the number of elements in this deque.
Definition: vecdeque.h:312
size_t BufferIndex(size_t pos) const noexcept
What index in the buffer does logical entry number pos have?
Definition: vecdeque.h:75
Data structure largely mimicking std::deque, but using single preallocated ring buffer.
Definition: vecdeque.h:24
void resize(size_t size)
Resize the deque to be exactly size size (adding default-constructed elements if needed).
Definition: vecdeque.h:110
T & front() noexcept
Get a mutable reference to the first element of the deque.
Definition: vecdeque.h:268
void pop_back()
Remove the last element of the deque.
Definition: vecdeque.h:260
T & back() noexcept
Get a mutable reference to the last element of the deque.
Definition: vecdeque.h:282
T & operator[](size_t idx) noexcept
Get a mutable reference to the element in the deque at the given index.
Definition: vecdeque.h:296
void push_front(T &&elem)
Move-construct a new element at the beginning of the deque.
Definition: vecdeque.h:247
const T & operator[](size_t idx) const noexcept
Get a const reference to the element in the deque at the given index.
Definition: vecdeque.h:303
memcpy(result.begin(), stream.data(), stream.size())
VecDeque(const VecDeque &other)
Copy-construct a deque.
Definition: vecdeque.h:180
bool empty() const noexcept
Test whether the contents of this deque is empty.
Definition: vecdeque.h:310
~VecDeque()
Destroy a deque.
Definition: vecdeque.h:129
VecDeque & operator=(const VecDeque &other)
Copy-assign a deque.
Definition: vecdeque.h:136
void emplace_back(Args &&... args)
Construct a new element at the end of the deque.
Definition: vecdeque.h:219
ArgsManager & args
Definition: bitcoind.cpp:277
size_t m_offset
m_buffer + m_offset points to first object in queue.
Definition: vecdeque.h:30
VecDeque() noexcept=default
void reserve(size_t capacity)
Increase the capacity to capacity.
Definition: vecdeque.h:206
void shrink_to_fit()
Make the capacity equal to the size.
Definition: vecdeque.h:212
size_t m_size
Number of objects in the container.
Definition: vecdeque.h:32
#define Assume(val)
Assume is the identity function.
Definition: check.h:97
VecDeque & operator=(VecDeque &&other) noexcept
Move-assign a deque.
Definition: vecdeque.h:173
VecDeque(VecDeque &&other) noexcept
Move-construct a deque.
Definition: vecdeque.h:182
const T & back() const noexcept
Get a const reference to the last element of the deque.
Definition: vecdeque.h:289
void pop_front()
Remove the first element of the deque.
Definition: vecdeque.h:250
void Reallocate(size_t capacity)
Definition: vecdeque.h:39
size_t m_capacity
The size of m_buffer, expressed as a multiple of the size of T.
Definition: vecdeque.h:34
void clear() noexcept
Resize the deque to be size 0.
Definition: vecdeque.h:126
void swap(VecDeque &other) noexcept
Swap two deques.
Definition: vecdeque.h:161
void ResizeDown(size_t size) noexcept
Specialization of resize() that can only shrink.
Definition: vecdeque.h:89
void push_front(const T &elem)
Copy-construct a new element at the beginning of the deque.
Definition: vecdeque.h:244
size_t capacity() const noexcept
Get the capacity of this deque (maximum size it can have without reallocating).
Definition: vecdeque.h:314
void push_back(const T &elem)
Copy-construct a new element at the end of the deque.
Definition: vecdeque.h:230
void push_back(T &&elem)
Move-construct a new element at the end of the deque.
Definition: vecdeque.h:227
bool friend operator==(const VecDeque &a, const VecDeque &b)
Equality comparison between two deques (only compares size+contents, not capacity).
Definition: vecdeque.h:185
T * m_buffer
Pointer to allocated memory.
Definition: vecdeque.h:27