Lines 18-23
Link Here
|
18 |
#define INCLUDE_PERFETTO_EXT_BASE_CIRCULAR_QUEUE_H_ |
18 |
#define INCLUDE_PERFETTO_EXT_BASE_CIRCULAR_QUEUE_H_ |
19 |
|
19 |
|
20 |
#include <stdint.h> |
20 |
#include <stdint.h> |
|
|
21 |
#include <stdlib.h> |
22 |
|
23 |
#include <cstddef> |
21 |
#include <iterator> |
24 |
#include <iterator> |
22 |
|
25 |
|
23 |
#include "perfetto/base/logging.h" |
26 |
#include "perfetto/base/logging.h" |
Lines 67-92
Link Here
|
67 |
ignore_result(generation); |
70 |
ignore_result(generation); |
68 |
} |
71 |
} |
69 |
|
72 |
|
70 |
T* operator->() { |
73 |
Iterator(const Iterator&) noexcept = default; |
|
|
74 |
Iterator& operator=(const Iterator&) noexcept = default; |
75 |
Iterator(Iterator&&) noexcept = default; |
76 |
Iterator& operator=(Iterator&&) noexcept = default; |
77 |
|
78 |
T* operator->() const { |
71 |
#if PERFETTO_DCHECK_IS_ON() |
79 |
#if PERFETTO_DCHECK_IS_ON() |
72 |
PERFETTO_DCHECK(generation_ == queue_->generation()); |
80 |
PERFETTO_DCHECK(generation_ == queue_->generation()); |
73 |
#endif |
81 |
#endif |
74 |
return queue_->Get(pos_); |
82 |
return queue_->Get(pos_); |
75 |
} |
83 |
} |
76 |
|
84 |
|
77 |
const T* operator->() const { |
85 |
T& operator*() const { return *(operator->()); } |
78 |
return const_cast<CircularQueue<T>::Iterator*>(this)->operator->(); |
|
|
79 |
} |
80 |
|
81 |
T& operator*() { return *(operator->()); } |
82 |
const T& operator*() const { return *(operator->()); } |
83 |
|
86 |
|
84 |
value_type& operator[](difference_type i) { return *(*this + i); } |
87 |
value_type& operator[](difference_type i) { return *(*this + i); } |
85 |
|
88 |
|
86 |
const value_type& operator[](difference_type i) const { |
|
|
87 |
return const_cast<CircularQueue<T>::Iterator&>(*this)[i]; |
88 |
} |
89 |
|
90 |
Iterator& operator++() { |
89 |
Iterator& operator++() { |
91 |
Add(1); |
90 |
Add(1); |
92 |
return *this; |
91 |
return *this; |
Lines 174-194
Link Here
|
174 |
#endif |
173 |
#endif |
175 |
}; |
174 |
}; |
176 |
|
175 |
|
177 |
CircularQueue(size_t initial_capacity = 1024) { Grow(initial_capacity); } |
176 |
explicit CircularQueue(size_t initial_capacity = 1024) { |
|
|
177 |
Grow(initial_capacity); |
178 |
} |
178 |
|
179 |
|
179 |
CircularQueue(CircularQueue&& other) noexcept { |
180 |
CircularQueue(CircularQueue&& other) noexcept |
180 |
// Copy all fields using the (private) default copy assignment operator. |
181 |
: entries_(std::move(other.entries_)), |
181 |
*this = other; |
182 |
capacity_(other.capacity_), |
|
|
183 |
begin_(other.begin_), |
184 |
end_(other.end_) { |
182 |
increment_generation(); |
185 |
increment_generation(); |
183 |
new (&other) CircularQueue(); // Reset the old queue so it's still usable. |
186 |
new (&other) CircularQueue(); // Reset the old queue so it's still usable. |
184 |
} |
187 |
} |
185 |
|
188 |
|
186 |
CircularQueue& operator=(CircularQueue&& other) { |
189 |
CircularQueue& operator=(CircularQueue&& other) noexcept { |
187 |
this->~CircularQueue(); // Destroy the current state. |
190 |
this->~CircularQueue(); // Destroy the current state. |
188 |
new (this) CircularQueue(std::move(other)); // Use the move ctor above. |
191 |
new (this) CircularQueue(std::move(other)); // Use the move ctor above. |
189 |
return *this; |
192 |
return *this; |
190 |
} |
193 |
} |
191 |
|
194 |
|
|
|
195 |
explicit CircularQueue(const CircularQueue& other) noexcept { |
196 |
Grow(other.capacity()); |
197 |
for (const auto& e : const_cast<CircularQueue&>(other)) |
198 |
emplace_back(e); |
199 |
PERFETTO_DCHECK(size() == other.size()); |
200 |
} |
201 |
|
202 |
CircularQueue& operator=(const CircularQueue& other) noexcept { |
203 |
this->~CircularQueue(); // Destroy the current state. |
204 |
new (this) CircularQueue(other); // Use the copy ctor above. |
205 |
return *this; |
206 |
} |
207 |
|
192 |
~CircularQueue() { |
208 |
~CircularQueue() { |
193 |
if (!entries_) { |
209 |
if (!entries_) { |
194 |
PERFETTO_DCHECK(empty()); |
210 |
PERFETTO_DCHECK(empty()); |
Lines 196-202
Link Here
|
196 |
} |
212 |
} |
197 |
clear(); // Invoke destructors on all alive entries. |
213 |
clear(); // Invoke destructors on all alive entries. |
198 |
PERFETTO_DCHECK(empty()); |
214 |
PERFETTO_DCHECK(empty()); |
199 |
free(entries_); |
|
|
200 |
} |
215 |
} |
201 |
|
216 |
|
202 |
template <typename... Args> |
217 |
template <typename... Args> |
Lines 220-225
Link Here
|
220 |
|
235 |
|
221 |
void clear() { erase_front(size()); } |
236 |
void clear() { erase_front(size()); } |
222 |
|
237 |
|
|
|
238 |
void shrink_to_fit() { |
239 |
// We only bother shrinking if we can fit in quarter of the capacity we are |
240 |
// currently using. Moreover, don't bother shrinking below 4096 elements as |
241 |
// that will cause a lot of reallocations for little benefit. |
242 |
if (size() > capacity() / 2 || capacity() <= 4096) { |
243 |
return; |
244 |
} |
245 |
ChangeCapacity(capacity() / 2); |
246 |
} |
247 |
|
223 |
T& at(size_t idx) { |
248 |
T& at(size_t idx) { |
224 |
PERFETTO_DCHECK(idx < size()); |
249 |
PERFETTO_DCHECK(idx < size()); |
225 |
return *Get(begin_ + idx); |
250 |
return *Get(begin_ + idx); |
Lines 248-256
Link Here
|
248 |
#endif |
273 |
#endif |
249 |
|
274 |
|
250 |
private: |
275 |
private: |
251 |
CircularQueue(const CircularQueue&) = delete; |
|
|
252 |
CircularQueue& operator=(const CircularQueue&) = default; |
253 |
|
254 |
void Grow(size_t new_capacity = 0) { |
276 |
void Grow(size_t new_capacity = 0) { |
255 |
// Capacity must be always a power of two. This allows Get() to use a simple |
277 |
// Capacity must be always a power of two. This allows Get() to use a simple |
256 |
// bitwise-AND for handling the wrapping instead of a full division. |
278 |
// bitwise-AND for handling the wrapping instead of a full division. |
Lines 260-268
Link Here
|
260 |
// On 32-bit systems this might hit the 4GB wall and overflow. We can't do |
282 |
// On 32-bit systems this might hit the 4GB wall and overflow. We can't do |
261 |
// anything other than crash in this case. |
283 |
// anything other than crash in this case. |
262 |
PERFETTO_CHECK(new_capacity > capacity_); |
284 |
PERFETTO_CHECK(new_capacity > capacity_); |
263 |
size_t malloc_size = new_capacity * sizeof(T); |
285 |
|
264 |
PERFETTO_CHECK(malloc_size > new_capacity); |
286 |
ChangeCapacity(new_capacity); |
265 |
auto* new_vec = static_cast<T*>(malloc(malloc_size)); |
287 |
} |
|
|
288 |
|
289 |
void ChangeCapacity(size_t new_capacity) { |
290 |
// We should still have enough space to fit all the elements in the queue. |
291 |
PERFETTO_CHECK(new_capacity >= size()); |
292 |
|
293 |
AlignedUniquePtr<T[]> new_vec = AlignedAllocTyped<T[]>(new_capacity); |
266 |
|
294 |
|
267 |
// Move all elements in the expanded array. |
295 |
// Move all elements in the expanded array. |
268 |
size_t new_size = 0; |
296 |
size_t new_size = 0; |
Lines 273-284
Link Here
|
273 |
// required to call the dtor for them. |
301 |
// required to call the dtor for them. |
274 |
for (uint64_t i = begin_; i < end_; i++) |
302 |
for (uint64_t i = begin_; i < end_; i++) |
275 |
Get(i)->~T(); |
303 |
Get(i)->~T(); |
276 |
free(entries_); // It's fine to free(nullptr) (for the ctor call case). |
|
|
277 |
|
304 |
|
278 |
begin_ = 0; |
305 |
begin_ = 0; |
279 |
end_ = new_size; |
306 |
end_ = new_size; |
280 |
capacity_ = new_capacity; |
307 |
capacity_ = new_capacity; |
281 |
entries_ = new_vec; |
308 |
entries_ = std::move(new_vec); |
282 |
} |
309 |
} |
283 |
|
310 |
|
284 |
inline T* Get(uint64_t pos) { |
311 |
inline T* Get(uint64_t pos) { |
Lines 290-296
Link Here
|
290 |
|
317 |
|
291 |
// Underlying storage. It's raw malloc-ed rather than being a unique_ptr<T[]> |
318 |
// Underlying storage. It's raw malloc-ed rather than being a unique_ptr<T[]> |
292 |
// to allow having uninitialized entries inside it. |
319 |
// to allow having uninitialized entries inside it. |
293 |
T* entries_ = nullptr; |
320 |
AlignedUniquePtr<T[]> entries_; |
294 |
size_t capacity_ = 0; // Number of allocated slots (NOT bytes) in |entries_|. |
321 |
size_t capacity_ = 0; // Number of allocated slots (NOT bytes) in |entries_|. |
295 |
|
322 |
|
296 |
// The |begin_| and |end_| indexes are monotonic and never wrap. Modular arith |
323 |
// The |begin_| and |end_| indexes are monotonic and never wrap. Modular arith |