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