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