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
12namespace spdlog {
13namespace details {
14template <typename T>
15class 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
22public:
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
99private:
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