FINAL CUT
char_ringbuffer.h
1 /***********************************************************************
2 * char_ringbuffer.h - Ring buffer for char elements *
3 * *
4 * This file is part of the FINAL CUT widget toolkit *
5 * *
6 * Copyright 2022-2026 Markus Gans *
7 * *
8 * FINAL CUT is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU Lesser General Public License as *
10 * published by the Free Software Foundation; either version 3 of *
11 * the License, or (at your option) any later version. *
12 * *
13 * FINAL CUT is distributed in the hope that it will be useful, but *
14 * WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU Lesser General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU Lesser General Public *
19 * License along with this program. If not, see *
20 * <http://www.gnu.org/licenses/>. *
21 ***********************************************************************/
22 
23 /* Inheritance diagram
24  * ═══════════════════
25  *
26  * ▕▔▔▔▔▔▔▔▔▔▔▔▔▔▏
27  * ▕ FRingBuffer ▏
28  * ▕▁▁▁▁▁▁▁▁▁▁▁▁▁▏
29  * ▲
30  * │
31  * ▕▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▏
32  * ▕ CharRingBuffer ▏
33  * ▕▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▏
34  */
35 
36 #ifndef CHARRINGBUFFER_H
37 #define CHARRINGBUFFER_H
38 
39 #if !defined (USE_FINAL_H) && !defined (COMPILE_FINAL_CUT)
40  #error "Only <final/final.h> can be included directly."
41 #endif
42 
43 #include <algorithm>
44 #include <array>
45 #include <cstring>
46 #include <string>
47 #include <utility>
48 
49 #include <final/util/fstring.h>
50 
51 namespace finalcut
52 {
53 
54 // class forward declaration
55 template <std::size_t>
57 
58 //----------------------------------------------------------------------
59 // class FRingBuffer
60 //----------------------------------------------------------------------
61 
62 template <typename T, std::size_t Capacity>
63 class FRingBuffer
64 {
65  public:
66  //------------------------------------------------------------------
67  // class ring_iterator
68  //------------------------------------------------------------------
69 
70  template<std::size_t N = Capacity>
71  struct ring_index
72  {
73 #if __cplusplus > 1 && __cplusplus >= 201703L
74 
75  static constexpr bool is_pow2 = (N & (N - 1)) == 0;
76 
77  static constexpr auto next (std::size_t current) noexcept -> std::size_t
78  {
79  if constexpr ( is_pow2 )
80  return (current + 1) & (N - 1);
81  else
82  return (current + 1) % N;
83  }
84 
85  static constexpr auto add ( std::size_t current
86  , std::size_t offset ) noexcept -> std::size_t
87  {
88  if constexpr ( is_pow2 )
89  return (current + offset) & (N - 1);
90  else
91  return (current + offset) % N;
92  }
93 
94 #else
95 
96  static constexpr auto next (std::size_t current) noexcept -> std::size_t
97  {
98  return (current + 1) % N;
99  }
100 
101  static constexpr auto add ( std::size_t current
102  , std::size_t offset ) noexcept -> std::size_t
103  {
104  return (current + offset) % N;
105  }
106 
107 #endif
108  };
109 
110  //------------------------------------------------------------------
111  // class ring_iterator
112  //------------------------------------------------------------------
113 
114  template <typename Type, std::size_t N>
116  {
117  public:
118  // Using-declarations
119  using iterator_category = std::forward_iterator_tag;
120  using value_type = Type;
121  using difference_type = std::ptrdiff_t;
122  using pointer = Type*;
123  using reference = Type&;
124 
125  explicit ring_iterator (pointer p, std::size_t start, std::size_t pos)
126  : ptr{p}
127  , offset{start}
128  , index{pos}
129  { }
130 
131  inline auto operator ++ () noexcept -> ring_iterator& // prefix
132  {
133  index++;
134  return *this;
135  }
136 
137  inline auto operator ++ (int) noexcept -> ring_iterator // postfix
138  {
139  ring_iterator i = *this;
140  index++;
141  return i;
142  }
143 
144  inline auto operator * () const noexcept -> reference
145  {
146 #if defined(__clang__)
147  #pragma clang diagnostic push
148  #if __has_warning("-Wunsafe-buffer-usage")
149  #pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
150  #endif
151 #endif
152  return ptr[ring_index<>::add(offset, index)];
153 #if defined(__clang__)
154  #pragma clang diagnostic pop
155 #endif
156  }
157 
158  inline auto operator -> () const noexcept -> pointer
159  {
160  return &**this;
161  }
162 
163  friend inline auto operator == ( const ring_iterator& lhs
164  , const ring_iterator& rhs ) noexcept -> bool
165  {
166  return lhs.index == rhs.index
167  && lhs.ptr == rhs.ptr
168  && lhs.offset == rhs.offset;
169  }
170 
171  friend inline auto operator != ( const ring_iterator& lhs
172  , const ring_iterator& rhs ) noexcept -> bool
173  {
174  return ! (lhs == rhs);
175  }
176 
177  private:
178  // Data members
179  pointer ptr{nullptr};
180  const std::size_t offset{0U};
181  std::size_t index{0U};
182 
183  // Friend Non-member operator functions
184  inline friend auto operator + ( const ring_iterator& iter
185  , std::ptrdiff_t size ) noexcept -> ring_iterator
186  {
187  auto tmp = iter;
188  tmp.index += std::size_t(size);
189  return tmp;
190  }
191  };
192 
193  // Using-declarations
196  using difference_type = std::ptrdiff_t;
197  using pointer = T*;
198  using reference = T&;
199  using const_reference = const T&;
200  using value_type = T;
201 
202  // Constructors
203  FRingBuffer()
204  : buffer()
205  { }
206 
207  virtual ~FRingBuffer() = default;
208 
209  // Overloaded operators
210  inline auto operator [] (std::size_t index) noexcept -> reference
211  {
212  static_assert ( Capacity > 0, "Ring buffer has no memory" );
213  return buffer[ring_index<>::add(head, index)];
214  }
215 
216  inline auto operator [] (std::size_t index) const noexcept -> const_reference
217  {
218  static_assert ( Capacity > 0, "Ring buffer has no memory" );
219  return buffer[ring_index<>::add(head, index)];
220  }
221 
222  // Accessors
223  virtual inline auto getClassName() const -> FString
224  {
225  return "FRingBuffer";
226  }
227 
228  inline auto getSize() const noexcept -> std::size_t
229  {
230  return elements;
231  }
232 
233  constexpr auto getCapacity() const noexcept -> std::size_t
234  {
235  return Capacity;
236  }
237 
238  inline auto begin() noexcept -> iterator
239  {
240  return iterator(buffer.data(), head, 0);
241  }
242 
243  inline auto begin() const noexcept -> const_iterator
244  {
245  return const_iterator(buffer.data(), head, 0);
246  }
247 
248  inline auto end() noexcept -> iterator
249  {
250  return iterator(buffer.data(), head, getSize());
251  }
252 
253  inline auto end() const noexcept -> const_iterator
254  {
255  return const_iterator(buffer.data(), head, getSize());
256  }
257 
258  inline auto front() noexcept -> reference
259  {
260  if ( isEmpty() )
261  return empty_element;
262 
263  return buffer[head];
264  }
265 
266  inline auto front() const noexcept -> const_reference
267  {
268  if ( isEmpty() )
269  return empty_element;
270 
271  return buffer[head];
272  }
273 
274  inline auto back() noexcept -> reference
275  {
276  if ( isEmpty() )
277  return empty_element;
278 
279  return buffer[last_index];
280  }
281 
282  inline auto back() const noexcept -> const_reference
283  {
284  if ( isEmpty() )
285  return empty_element;
286 
287  return buffer[last_index];
288  }
289 
290  // Mutators
291  inline void clear() noexcept
292  {
293  head = 0U;
294  tail = 0U;
295  last_index = Capacity - 1;
296  elements = 0U;
297  }
298 
299  // Predicates
300  constexpr auto isEmpty() const noexcept -> bool
301  {
302  return elements == 0;
303  }
304 
305  constexpr auto hasData() const noexcept -> bool
306  {
307  return ! isEmpty();
308  }
309 
310  constexpr auto isFull() const noexcept -> bool
311  {
312  return elements == Capacity;
313  }
314 
315  // Methods
316  inline void push (const T& item) noexcept
317  {
318  if ( isFull() )
319  return;
320 
321  static_assert ( Capacity > 0, "Ring buffer has no memory" );
322  buffer[tail] = item;
323  last_index = tail;
324  tail = ring_index<>::next(tail);
325  elements++;
326  }
327 
328  inline void push_back (const T& item) noexcept
329  {
330  push (item);
331  }
332 
333  template <typename... Args>
334  inline void emplace (Args&&... args)
335  {
336  if ( isFull() )
337  return;
338 
339  static_assert ( Capacity > 0, "Ring buffer has no memory" );
340  buffer[tail] = T(std::forward<Args>(args)...);
341  last_index = tail;
342  tail = ring_index<>::next(tail);
343  elements++;
344  }
345 
346  template <typename... Args>
347  inline void emplace_back (Args&&... args)
348  {
349  emplace (std::forward<Args>(args)...);
350  }
351 
352  inline void pop() noexcept
353  {
354  if ( isEmpty() )
355  return;
356 
357  static_assert ( Capacity > 0, "Ring buffer has no memory" );
358  head = ring_index<>::next(head);
359  elements--;
360  }
361 
362  inline void pop_front() noexcept
363  {
364  pop();
365  }
366 
367  inline void pop (std::size_t s) noexcept
368  {
369  if ( isEmpty() )
370  return;
371 
372  static_assert ( Capacity > 0, "Ring buffer has no memory" );
373  s = std::min(s, elements);
374  head = ring_index<>::add(head, s);
375  elements -= s;
376  }
377 
378  private:
379  // Data members
380  std::size_t head{0U};
381  std::size_t tail{0U};
382  std::size_t last_index{0U};
383  std::size_t elements{0U};
384  std::array<value_type, Capacity> buffer;
385  value_type empty_element{}; // Fallback value
386 
387  // Friend classes
388  friend class CharRingBuffer<Capacity>;
389 };
390 
391 
392 //----------------------------------------------------------------------
393 // class CharRingBuffer
394 //----------------------------------------------------------------------
395 
396 template <std::size_t Capacity>
397 class CharRingBuffer final : public FRingBuffer<char, Capacity>
398 {
399  public:
400  // Using-declarations
407 
408  // Accessor
409  inline auto getClassName() const -> FString override
410  {
411  return "CharRingBuffer";
412  }
413 
414  // Method
415  auto strncmp_front ( const char* string
416  , std::size_t length ) const noexcept -> bool
417  {
418 #if defined(__clang__)
419  #pragma clang diagnostic push
420  #if __has_warning("-Wunsafe-buffer-usage")
421  #pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
422  #endif
423 #endif
424  if ( length == 0 )
425  return true;
426 
427  if ( length > getSize() )
428  return false;
429 
430  const auto* buf = buffer.data();
431 
432  if ( tail > head )
433  return std::memcmp(string, buf + head, length) == 0;
434 
435  const auto l1 = std::min(length, Capacity - head);
436 
437  if ( std::memcmp(string, buf + head, l1) != 0 )
438  return false;
439 
440  const auto l2 = length - l1;
441  return l2 == 0 || std::memcmp(string + l1, buf, l2) == 0;
442 #if defined(__clang__)
443  #pragma clang diagnostic pop
444 #endif
445  }
446 
447 };
448 
449 } // namespace finalcut
450 
451 #endif // CHARRINGBUFFER_H
Definition: class_template.cpp:25
Definition: char_ringbuffer.h:71
Definition: fstring.h:82
Definition: char_ringbuffer.h:115
Definition: ftermoutput.h:69
Definition: char_ringbuffer.h:56