scc  2024.06
SystemC components library
ring_buffer.h
Go to the documentation of this file.
1 /*
2 The MIT License (MIT)
3 
4 Copyright (c) 1999-2022 Mircea Neacsu
5 
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12 
13 The above copyright notice and this permission notice shall be included in all
14 copies or substantial portions of the Software.
15 
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 SOFTWARE.
23  */
31 #pragma once
32 
33 #include <cassert>
34 #include <memory>
35 #include <stdexcept>
36 #include <vector>
37 
38 namespace util {
39 
41 template <class T> class ring_buffer {
42 public:
44  template <bool C_> class iterator_type {
45  public:
46  // Following typedefs must exist to allow instantiation of std::iterator_traits
47  using difference_type = std::ptrdiff_t;
48  using value_type = T;
49  using reference = typename std::conditional_t<C_, T const&, T&>;
50  using pointer = typename std::conditional_t<C_, T const*, T*>;
51  using iterator_category = std::bidirectional_iterator_tag;
52 
55  : ring(nullptr)
56  , pos(0) {}
57 
59  reference operator*() {
60  if(pos == (size_t)(-1))
61  throw std::range_error("Ring buffer iterator at end!");
62  return ring->buf[pos];
63  }
64 
66  const reference operator*() const {
67  if(pos == (size_t)(-1))
68  throw std::range_error("Ring buffer iterator at end!");
69  return ring->buf[pos];
70  }
71 
73  pointer operator->() {
74  if(pos == (size_t)(-1))
75  throw std::range_error("Ring buffer iterator at end!");
76  return &ring->buf[pos];
77  }
78 
80  const pointer operator->() const {
81  if(pos == (size_t)(-1))
82  throw std::range_error("Ring buffer iterator at end!");
83  return &ring->buf[pos];
84  }
85 
88  iterator_type<C_> tmp = *this;
89  pos = ring->increment(pos);
90  return tmp;
91  }
92 
95  pos = ring->increment(pos);
96  return *this;
97  }
98 
101  iterator_type<C_> tmp = *this;
102  pos = ring->decrement(pos);
103  return tmp;
104  }
105 
108  pos = ring->decrement(pos);
109  return *this;
110  }
111 
113  bool operator==(iterator_type<C_> const& it) const { return (ring == it.ring) && (pos == it.pos); }
114 
116  bool operator!=(iterator_type<C_> const& it) const { return !operator==(it); }
117 
120  if(this != &rhs) {
121  ring = rhs.ring;
122  pos = rhs.pos;
123  }
124  return *this;
125  }
126 
128  iterator_type<C_> operator+(size_t inc) const {
129  iterator_type<C_> tmp = *this;
130  tmp.pos = ring->add(pos, inc);
131  return tmp;
132  }
133 
136  pos = ring->add(pos, inc);
137  return *this;
138  }
139 
141  iterator_type<C_> operator-(size_t dec) const {
142  iterator_type<C_> tmp = *this;
143  tmp.pos = ring->subtract(pos, dec);
144  return tmp;
145  }
146 
149  pos = ring->subtract(pos, dec);
150  return *this;
151  }
152 
154  ptrdiff_t operator-(iterator_type<C_> const& other) const {
155  assert(ring == other.ring);
156  size_t p1 = (pos != -1) ? pos : (ring->back_idx == ring->front_idx) ? ring->sz : ring->back_idx;
157  size_t p2 = (other.pos != -1) ? other.pos : (ring->back_idx == ring->front_idx) ? ring->sz : ring->back_idx;
158  if(p1 >= p2)
159  return p1 - p2;
160  else
161  return ring->cap - p2 + p1;
162  }
163 
164  private:
165  iterator_type(ring_buffer const* ring_, size_t pos_)
166  : ring(ring_)
167  , pos(pos_) {}
168 
169  ring_buffer const* ring;
170  size_t pos;
171  friend class ring_buffer;
172  };
173 
174  typedef iterator_type<false> iterator;
175  typedef iterator_type<true> const_iterator;
176 
179  : buf(std::unique_ptr<T[]>(new T[size]))
180  , cap(size)
181  , front_idx(0)
182  , back_idx(0)
183  , sz(0) {}
184 
187  : cap(0)
188  , front_idx(0)
189  , back_idx(0)
190  , sz(0) {}
191 
193  ring_buffer(ring_buffer const& other) = delete;
194 
196  ring_buffer(ring_buffer&& other) = delete;
197 
199  ring_buffer(std::initializer_list<T> il)
200  : buf(std::unique_ptr<T[]>(new T[il.size()]))
201  , cap(il.size())
202  , front_idx(0)
203  , back_idx(0)
204  , sz(il.size()) {
205  size_t i = 0;
206  for(const auto v : il)
207  buf[i++] = std::move(v);
208  }
209 
211  ring_buffer& operator=(ring_buffer const& rhs) = delete;
212 
213  ring_buffer& operator=(ring_buffer&& rhs) = delete;
214 
216  bool operator==(ring_buffer<T> const& other) const noexcept {
217  if(cap == other.cap && sz == other.sz) {
218  for(size_t i = 0; i < sz; i++) {
219  size_t idx1 = (front_idx + i) % cap;
220  size_t idx2 = (other.front_idx + i) % other.cap;
221  if(buf[idx1] != other.buf[idx2])
222  return false;
223  }
224  return true;
225  }
226  return false;
227  }
228 
230  bool operator!=(ring_buffer<T> const& other) const noexcept { return !operator==(other); }
231 
233  operator std::vector<T>() const {
234  std::vector<T> v;
235  v.reserve(sz);
236  std::copy(std::begin(*this), std::end(*this), std::back_inserter(v));
237  return v;
238  }
239 
241  void push_back(T const& item) {
242  if(!cap)
243  return; // container not allocated
244  throw std::length_error("ring_buffer: trying to add an element to a buffer of size 0");
245  if(sz && back_idx == front_idx)
246  throw std::overflow_error("ring_buffer: overflow error");
247  sz++;
248  buf[back_idx] = item;
249  back_idx = (back_idx + 1) % cap;
250  }
251 
252  void push_back(T&& item) {
253  if(!cap)
254  return; // container not allocated
255  throw std::length_error("ring_buffer: trying to add an element to a buffer of size 0");
256  if(sz && back_idx == front_idx)
257  throw std::overflow_error("ring_buffer: overflow error");
258  sz++;
259  buf[back_idx] = std::move(item);
260  back_idx = (back_idx + 1) % cap;
261  }
262 
264  iterator begin() noexcept { return iterator(this, front_idx); }
265 
267  const_iterator begin() const noexcept { return const_iterator(this, front_idx); }
268 
270  const_iterator cbegin() const noexcept { return const_iterator(this, front_idx); }
271 
273  iterator end() noexcept { return iterator(this, (size_t)(-1)); }
274 
276  const_iterator end() const noexcept { return const_iterator(this, (size_t)(-1)); }
277 
279  const_iterator cend() const noexcept { return const_iterator(this, (size_t)(-1)); }
280 
282  void pop_front() {
283  if(!sz)
284  throw std::underflow_error("ring_buffer: buffer is empty");
285  front_idx = (front_idx + 1) % cap;
286  sz--;
287  }
288 
290  T& front() {
291  if(!sz)
292  throw std::underflow_error("ring_buffer: buffer is empty");
293  return buf[front_idx];
294  }
295 
297  T const& front() const {
298  if(!sz)
299  throw std::underflow_error("ring_buffer: buffer is empty");
300  return buf[front_idx];
301  }
302 
304  T& back() {
305  if(!sz)
306  throw std::underflow_error("ring_buffer: buffer is empty");
307  return buf[(back_idx + cap - 1) % cap];
308  }
309 
311  T const& back() const {
312  if(!sz)
313  throw std::underflow_error("ring_buffer: buffer is empty");
314  return buf[(back_idx + cap - 1) % cap];
315  }
316 
318  void clear(void) {
319  for(size_t i = 0; i < sz; i++) {
320  buf[(front_idx + i) % cap].~T();
321  }
322 
323  front_idx = back_idx;
324  sz = 0;
325  }
326 
328  bool empty(void) const noexcept { return (sz == 0); }
329 
331  bool full(void) const noexcept { return (sz == cap); }
332 
334  size_t capacity(void) const noexcept { return cap; };
335 
337  void resize(size_t new_cap) {
338  if(new_cap < sz)
339  throw std::bad_array_new_length();
340  std::unique_ptr<T[]> newbuf(new_cap ? new T[new_cap] : nullptr);
341  if(new_cap) {
342  for(size_t i = 0; i < sz; i++) {
343  newbuf[i] = std::move(buf[(front_idx + i) % cap]);
344  }
345  } else
346  sz = 0;
347  cap = new_cap;
348  buf.swap(newbuf);
349  front_idx = 0;
350  back_idx = cap ? (front_idx + sz) % cap : 0;
351  }
352 
354  size_t size() const noexcept { return sz; }
355 
356 private:
358  size_t increment(size_t pos) const noexcept {
359  if(cap && pos != (size_t)-1)
360  pos = (pos + 1) % cap;
361  if(pos == back_idx)
362  pos = (size_t)-1;
363  return pos;
364  }
365 
367  size_t decrement(size_t pos) const noexcept {
368  if(cap) {
369  if(pos == (size_t)-1)
370  pos = (back_idx + cap - 1) % cap;
371  else if(pos != front_idx)
372  pos = (pos + cap - 1) % cap;
373  }
374  return pos;
375  }
376 
378  size_t add(size_t oldpos, size_t delta) const noexcept {
379  if(cap && oldpos != -1) {
380  size_t np = oldpos + (delta % cap);
381  if(np >= cap && np >= back_idx + cap)
382  np = (size_t)(-1);
383  else
384  np %= cap;
385  return np;
386  }
387  return oldpos;
388  }
389 
391  size_t subtract(size_t oldpos, size_t delta) const noexcept {
392  if(cap) {
393  size_t np = (oldpos == (size_t)-1 ? back_idx : oldpos) - (delta % cap) + cap;
394  if(np < front_idx)
395  np = front_idx;
396  else
397  np %= cap;
398  return np;
399  }
400  return oldpos;
401  }
402 
403  std::unique_ptr<T[]> buf;
404  size_t front_idx, back_idx, cap, sz;
405 };
406 
407 } // namespace util
Iterator through the circular buffer.
Definition: ring_buffer.h:44
iterator_type< C_ > & operator+=(size_t inc)
Addition assignment operator.
Definition: ring_buffer.h:135
ptrdiff_t operator-(iterator_type< C_ > const &other) const
Difference operator.
Definition: ring_buffer.h:154
iterator_type< C_ > & operator++()
Increment operator (prefix)
Definition: ring_buffer.h:94
bool operator==(iterator_type< C_ > const &it) const
Equality comparison.
Definition: ring_buffer.h:113
const reference operator*() const
Dereference operator (const version)
Definition: ring_buffer.h:66
iterator_type< C_ > operator++(int)
Increment operator (postfix)
Definition: ring_buffer.h:87
iterator_type< C_ > operator-(size_t dec) const
Subtraction operator.
Definition: ring_buffer.h:141
iterator_type()
Default constructor.
Definition: ring_buffer.h:54
iterator_type< C_ > operator+(size_t inc) const
Addition operator.
Definition: ring_buffer.h:128
iterator_type< C_ > operator--(int)
Decrement operator (postfix)
Definition: ring_buffer.h:100
iterator_type< C_ > & operator=(iterator_type< C_ > const &rhs)
Assignment operator.
Definition: ring_buffer.h:119
iterator_type< C_ > & operator-=(size_t dec)
Subtraction assignment operator.
Definition: ring_buffer.h:148
reference operator*()
Dereference operator.
Definition: ring_buffer.h:59
const pointer operator->() const
Object pointer (const version)
Definition: ring_buffer.h:80
bool operator!=(iterator_type< C_ > const &it) const
Inequality comparison.
Definition: ring_buffer.h:116
iterator_type< C_ > & operator--()
Decrement operator (prefix)
Definition: ring_buffer.h:107
pointer operator->()
Object pointer.
Definition: ring_buffer.h:73
Circular buffer.
Definition: ring_buffer.h:41
bool operator!=(ring_buffer< T > const &other) const noexcept
Inequality operator.
Definition: ring_buffer.h:230
bool full(void) const noexcept
Return true if buffer is full.
Definition: ring_buffer.h:331
ring_buffer(std::initializer_list< T > il)
Initializer list constructor.
Definition: ring_buffer.h:199
const_iterator cend() const noexcept
Return an iterator pointing past the last (newest) element in buffer.
Definition: ring_buffer.h:279
ring_buffer(size_t size)
Constructor.
Definition: ring_buffer.h:178
void clear(void)
Remove all elements from buffer.
Definition: ring_buffer.h:318
void resize(size_t new_cap)
(Re)allocate buffer with a different capacity
Definition: ring_buffer.h:337
const_iterator cbegin() const noexcept
Return a const iterator pointing to first (oldest) element in buffer.
Definition: ring_buffer.h:270
size_t size() const noexcept
Return number of elements in buffer.
Definition: ring_buffer.h:354
T const & back() const
Return reference to last (newest) element in buffer.
Definition: ring_buffer.h:311
iterator end() noexcept
Return an iterator pointing past the last (newest) element in buffer.
Definition: ring_buffer.h:273
const_iterator end() const noexcept
Return a const iterator pointing past the last (newest) element in buffer.
Definition: ring_buffer.h:276
ring_buffer(ring_buffer &&other)=delete
Copy constructor.
T & front()
Return a reference to first (oldest) element in buffer.
Definition: ring_buffer.h:290
void pop_front()
Remove oldest element from buffer.
Definition: ring_buffer.h:282
ring_buffer & operator=(ring_buffer const &rhs)=delete
Assignment operator.
ring_buffer()
Default constructor.
Definition: ring_buffer.h:186
iterator begin() noexcept
Return an iterator pointing to first (oldest) element in buffer.
Definition: ring_buffer.h:264
T & back()
Return reference to last (newest) element in buffer.
Definition: ring_buffer.h:304
bool operator==(ring_buffer< T > const &other) const noexcept
Equality operator.
Definition: ring_buffer.h:216
const_iterator begin() const noexcept
Return a const iterator pointing to first (oldest) element in buffer.
Definition: ring_buffer.h:267
ring_buffer(ring_buffer const &other)=delete
Copy constructor.
void push_back(T const &item)
Inserts new element in buffer.
Definition: ring_buffer.h:241
size_t capacity(void) const noexcept
Return maximum buffer size.
Definition: ring_buffer.h:334
T const & front() const
Return a reference to first (oldest) element in buffer.
Definition: ring_buffer.h:297
bool empty(void) const noexcept
Return true if buffer is empty.
Definition: ring_buffer.h:328
SCC common utilities.
Definition: bit_field.h:30