1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. |
2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) |
3 | |
4 | // circular q view of std::vector. |
5 | #pragma once |
6 | |
7 | #include <cassert> |
8 | #include <vector> |
9 | |
10 | #include "spdlog/common.h" |
11 | |
12 | namespace spdlog { |
13 | namespace details { |
14 | template <typename T> |
15 | class circular_q { |
16 | size_t max_items_ = 0; |
17 | typename std::vector<T>::size_type head_ = 0; |
18 | typename std::vector<T>::size_type tail_ = 0; |
19 | size_t overrun_counter_ = 0; |
20 | std::vector<T> v_; |
21 | |
22 | public: |
23 | using value_type = T; |
24 | |
25 | // empty ctor - create a disabled queue with no elements allocated at all |
26 | circular_q() = default; |
27 | |
28 | explicit circular_q(size_t max_items) |
29 | : max_items_(max_items + 1) // one item is reserved as marker for full q |
30 | , |
31 | v_(max_items_) {} |
32 | |
33 | circular_q(const circular_q &) = default; |
34 | circular_q &operator=(const circular_q &) = default; |
35 | |
36 | // move cannot be default, |
37 | // since we need to reset head_, tail_, etc to zero in the moved object |
38 | circular_q(circular_q &&other) SPDLOG_NOEXCEPT { copy_moveable(other: std::move(other)); } |
39 | |
40 | circular_q &operator=(circular_q &&other) SPDLOG_NOEXCEPT { |
41 | copy_moveable(other: std::move(other)); |
42 | return *this; |
43 | } |
44 | |
45 | // push back, overrun (oldest) item if no room left |
46 | void push_back(T &&item) { |
47 | if (max_items_ > 0) { |
48 | v_[tail_] = std::move(item); |
49 | tail_ = (tail_ + 1) % max_items_; |
50 | |
51 | if (tail_ == head_) // overrun last item if full |
52 | { |
53 | head_ = (head_ + 1) % max_items_; |
54 | ++overrun_counter_; |
55 | } |
56 | } |
57 | } |
58 | |
59 | // Return reference to the front item. |
60 | // If there are no elements in the container, the behavior is undefined. |
61 | const T &front() const { return v_[head_]; } |
62 | |
63 | T &front() { return v_[head_]; } |
64 | |
65 | // Return number of elements actually stored |
66 | size_t size() const { |
67 | if (tail_ >= head_) { |
68 | return tail_ - head_; |
69 | } else { |
70 | return max_items_ - (head_ - tail_); |
71 | } |
72 | } |
73 | |
74 | // Return const reference to item by index. |
75 | // If index is out of range 0…size()-1, the behavior is undefined. |
76 | const T &at(size_t i) const { |
77 | assert(i < size()); |
78 | return v_[(head_ + i) % max_items_]; |
79 | } |
80 | |
81 | // Pop item from front. |
82 | // If there are no elements in the container, the behavior is undefined. |
83 | void pop_front() { head_ = (head_ + 1) % max_items_; } |
84 | |
85 | bool empty() const { return tail_ == head_; } |
86 | |
87 | bool full() const { |
88 | // head is ahead of the tail by 1 |
89 | if (max_items_ > 0) { |
90 | return ((tail_ + 1) % max_items_) == head_; |
91 | } |
92 | return false; |
93 | } |
94 | |
95 | size_t overrun_counter() const { return overrun_counter_; } |
96 | |
97 | void reset_overrun_counter() { overrun_counter_ = 0; } |
98 | |
99 | private: |
100 | // copy from other&& and reset it to disabled state |
101 | void copy_moveable(circular_q &&other) SPDLOG_NOEXCEPT { |
102 | max_items_ = other.max_items_; |
103 | head_ = other.head_; |
104 | tail_ = other.tail_; |
105 | overrun_counter_ = other.overrun_counter_; |
106 | v_ = std::move(other.v_); |
107 | |
108 | // put &&other in disabled, but valid state |
109 | other.max_items_ = 0; |
110 | other.head_ = other.tail_ = 0; |
111 | other.overrun_counter_ = 0; |
112 | } |
113 | }; |
114 | } // namespace details |
115 | } // namespace spdlog |
116 | |