quill
TransitEventBuffer.h
1 
7 #pragma once
8 
9 #include "quill/backend/TransitEvent.h"
10 #include "quill/bundled/fmt/format.h" // for assert_fail
11 #include "quill/core/Attributes.h"
12 #include "quill/core/MathUtilities.h"
13 
14 QUILL_BEGIN_NAMESPACE
15 
16 namespace detail
17 {
18 
20 {
21 public:
22  explicit TransitEventBuffer(size_t initial_capacity)
23  : _initial_capacity(next_power_of_two(initial_capacity)),
24  _capacity(_initial_capacity),
25  _storage(std::make_unique<TransitEvent[]>(_capacity)),
26  _mask(_capacity - 1u)
27  {
28  }
29 
30  TransitEventBuffer(TransitEventBuffer const&) = delete;
31  TransitEventBuffer& operator=(TransitEventBuffer const&) = delete;
32 
33  // Move constructor
34  TransitEventBuffer(TransitEventBuffer&& other) noexcept
35  : _initial_capacity(other._initial_capacity),
36  _capacity(other._capacity),
37  _storage(std::move(other._storage)),
38  _mask(other._mask),
39  _reader_pos(other._reader_pos),
40  _writer_pos(other._writer_pos)
41  {
42  other._capacity = 0;
43  other._mask = 0;
44  other._reader_pos = 0;
45  other._writer_pos = 0;
46  }
47 
48  // Move assignment operator
49  TransitEventBuffer& operator=(TransitEventBuffer&& other) noexcept
50  {
51  if (this != &other)
52  {
53  _initial_capacity = other._initial_capacity;
54  _capacity = other._capacity;
55  _storage = std::move(other._storage);
56  _mask = other._mask;
57  _reader_pos = other._reader_pos;
58  _writer_pos = other._writer_pos;
59 
60  other._capacity = 0;
61  other._mask = 0;
62  other._reader_pos = 0;
63  other._writer_pos = 0;
64  }
65  return *this;
66  }
67 
68  QUILL_NODISCARD QUILL_ATTRIBUTE_HOT TransitEvent* front() noexcept
69  {
70  if (_reader_pos == _writer_pos)
71  {
72  return nullptr;
73  }
74  return &_storage[_reader_pos & _mask];
75  }
76 
77  QUILL_ATTRIBUTE_HOT void pop_front() noexcept { ++_reader_pos; }
78 
79  QUILL_NODISCARD QUILL_ATTRIBUTE_HOT TransitEvent* back() noexcept
80  {
81  if (_capacity == size())
82  {
83  // Buffer is full, need to expand
84  _expand();
85  }
86  return &_storage[_writer_pos & _mask];
87  }
88 
89  QUILL_ATTRIBUTE_HOT void push_back() noexcept { ++_writer_pos; }
90 
91  QUILL_NODISCARD QUILL_ATTRIBUTE_HOT size_t size() const noexcept
92  {
93  return _writer_pos - _reader_pos;
94  }
95 
96  QUILL_NODISCARD QUILL_ATTRIBUTE_HOT size_t capacity() const noexcept { return _capacity; }
97 
98  QUILL_NODISCARD QUILL_ATTRIBUTE_HOT bool empty() const noexcept
99  {
100  return _reader_pos == _writer_pos;
101  }
102 
103  void request_shrink() noexcept { _shrink_requested = true; }
104 
105  void try_shrink()
106  {
107  // we only shrink empty buffers
108  if (_shrink_requested && empty())
109  {
110  if (_capacity > _initial_capacity)
111  {
112  _storage = std::make_unique<TransitEvent[]>(_initial_capacity);
113  _capacity = _initial_capacity;
114  _mask = _capacity - 1;
115  _writer_pos = 0;
116  _reader_pos = 0;
117  }
118 
119  _shrink_requested = false;
120  }
121  }
122 
123 private:
124  void _expand()
125  {
126  size_t const new_capacity = _capacity * 2;
127 
128  auto new_storage = std::make_unique<TransitEvent[]>(new_capacity);
129 
130  // Move existing elements from the old storage to the new storage.
131  // Since the buffer is full, this moves all the previous TransitEvents, preserving their order.
132  // The reader position and mask are used to handle the circular buffer's wraparound.
133  size_t const current_size = size();
134  for (size_t i = 0; i < current_size; ++i)
135  {
136  new_storage[i] = std::move(_storage[(_reader_pos + i) & _mask]);
137  }
138 
139  _storage = std::move(new_storage);
140  _capacity = new_capacity;
141  _mask = _capacity - 1;
142  _writer_pos = current_size;
143  _reader_pos = 0;
144  }
145 
146  size_t _initial_capacity;
147  size_t _capacity;
148  std::unique_ptr<TransitEvent[]> _storage;
149  size_t _mask;
150  size_t _reader_pos{0};
151  size_t _writer_pos{0};
152  bool _shrink_requested{false};
153 };
154 
155 } // namespace detail
156 
157 QUILL_END_NAMESPACE
QUILL_NODISCARD T next_power_of_two(T n) noexcept
Round up to the next power of 2.
Definition: MathUtilities.h:46
Definition: TransitEvent.h:32
Definition: TransitEventBuffer.h:19
Setups a signal handler to handle fatal signals.
Definition: BackendManager.h:24