scc 2025.09
SystemC components library
ring_buffer.h
Go to the documentation of this file.
1/*
2The MIT License (MIT)
3
4Copyright (c) 1999-2022 Mircea Neacsu
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23 */
31#pragma once
32
33#include <cassert>
34#include <memory>
35#include <stdexcept>
36#include <vector>
37
38namespace util {
39
41template <class T> class ring_buffer {
42public:
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 throw std::length_error("ring_buffer: trying to add an element to a buffer of size 0");
244 if(sz && back_idx == front_idx)
245 throw std::overflow_error("ring_buffer: overflow error");
246 sz++;
247 buf[back_idx] = item;
248 back_idx = (back_idx + 1) % cap;
249 }
250
251 void push_back(T&& item) {
252 if(!cap)
253 throw std::length_error("ring_buffer: trying to add an element to a buffer of size 0");
254 if(sz && back_idx == front_idx)
255 throw std::overflow_error("ring_buffer: overflow error");
256 sz++;
257 buf[back_idx] = std::move(item);
258 back_idx = (back_idx + 1) % cap;
259 }
260
262 iterator begin() noexcept { return iterator(this, front_idx); }
263
265 const_iterator begin() const noexcept { return const_iterator(this, front_idx); }
266
268 const_iterator cbegin() const noexcept { return const_iterator(this, front_idx); }
269
271 iterator end() noexcept { return iterator(this, (size_t)(-1)); }
272
274 const_iterator end() const noexcept { return const_iterator(this, (size_t)(-1)); }
275
277 const_iterator cend() const noexcept { return const_iterator(this, (size_t)(-1)); }
278
280 void pop_front() {
281 if(!sz)
282 throw std::underflow_error("ring_buffer: buffer is empty");
283 front_idx = (front_idx + 1) % cap;
284 sz--;
285 }
286
288 T& front() {
289 if(!sz)
290 throw std::underflow_error("ring_buffer: buffer is empty");
291 return buf[front_idx];
292 }
293
295 T const& front() const {
296 if(!sz)
297 throw std::underflow_error("ring_buffer: buffer is empty");
298 return buf[front_idx];
299 }
300
302 T& back() {
303 if(!sz)
304 throw std::underflow_error("ring_buffer: buffer is empty");
305 return buf[(back_idx + cap - 1) % cap];
306 }
307
309 T const& back() const {
310 if(!sz)
311 throw std::underflow_error("ring_buffer: buffer is empty");
312 return buf[(back_idx + cap - 1) % cap];
313 }
314
316 void clear(void) {
317 for(size_t i = 0; i < sz; i++) {
318 buf[(front_idx + i) % cap].~T();
319 }
320
321 front_idx = back_idx;
322 sz = 0;
323 }
324
326 bool empty(void) const noexcept { return (sz == 0); }
327
329 bool full(void) const noexcept { return (sz == cap); }
330
332 size_t capacity(void) const noexcept { return cap; };
333
335 void resize(size_t new_cap) {
336 if(new_cap < sz)
337 throw std::bad_array_new_length();
338 std::unique_ptr<T[]> newbuf(new_cap ? new T[new_cap] : nullptr);
339 if(new_cap) {
340 for(size_t i = 0; i < sz; i++) {
341 newbuf[i] = std::move(buf[(front_idx + i) % cap]);
342 }
343 } else
344 sz = 0;
345 cap = new_cap;
346 buf.swap(newbuf);
347 front_idx = 0;
348 back_idx = cap ? (front_idx + sz) % cap : 0;
349 }
350
352 size_t size() const noexcept { return sz; }
353
354private:
356 size_t increment(size_t pos) const noexcept {
357 if(cap && pos != (size_t)-1)
358 pos = (pos + 1) % cap;
359 if(pos == back_idx)
360 pos = (size_t)-1;
361 return pos;
362 }
363
365 size_t decrement(size_t pos) const noexcept {
366 if(cap) {
367 if(pos == (size_t)-1)
368 pos = (back_idx + cap - 1) % cap;
369 else if(pos != front_idx)
370 pos = (pos + cap - 1) % cap;
371 }
372 return pos;
373 }
374
376 size_t add(size_t oldpos, size_t delta) const noexcept {
377 if(cap && oldpos != -1) {
378 size_t np = oldpos + (delta % cap);
379 if(np >= cap && np >= back_idx + cap)
380 np = (size_t)(-1);
381 else
382 np %= cap;
383 return np;
384 }
385 return oldpos;
386 }
387
389 size_t subtract(size_t oldpos, size_t delta) const noexcept {
390 if(cap) {
391 size_t np = (oldpos == (size_t)-1 ? back_idx : oldpos) - (delta % cap) + cap;
392 if(np < front_idx)
393 np = front_idx;
394 else
395 np %= cap;
396 return np;
397 }
398 return oldpos;
399 }
400
401 std::unique_ptr<T[]> buf;
402 size_t front_idx, back_idx, cap, sz;
403};
404
405} // namespace util
iterator_type< C_ > & operator--()
Decrement operator (prefix).
iterator_type< C_ > operator++(int)
Increment operator (postfix).
Definition ring_buffer.h:87
iterator_type< C_ > & operator++()
Increment operator (prefix).
Definition ring_buffer.h:94
iterator_type< C_ > operator--(int)
Decrement operator (postfix).
iterator_type< C_ > & operator+=(size_t inc)
Addition assignment operator.
ptrdiff_t operator-(iterator_type< C_ > const &other) const
Difference operator.
bool operator==(iterator_type< C_ > const &it) const
Equality comparison.
iterator_type< C_ > & operator=(iterator_type< C_ > const &rhs)
Assignment operator.
const reference operator*() const
Dereference operator (const version).
Definition ring_buffer.h:66
iterator_type< C_ > & operator-=(size_t dec)
Subtraction assignment operator.
iterator_type< C_ > operator+(size_t inc) const
Addition operator.
iterator_type()
Default constructor.
Definition ring_buffer.h:54
iterator_type< C_ > operator-(size_t dec) const
Subtraction operator.
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.
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.
T & front()
Return a reference to first (oldest) element in buffer.
bool full(void) const noexcept
Return true if buffer is full.
ring_buffer(std::initializer_list< T > il)
Initializer list constructor.
const_iterator cend() const noexcept
Return an iterator pointing past the last (newest) element in buffer.
ring_buffer(size_t size)
Constructor.
void clear(void)
Remove all elements from buffer.
T const & back() const
Return reference to last (newest) element in buffer.
void resize(size_t new_cap)
(Re)allocate buffer with a different capacity
const_iterator cbegin() const noexcept
Return a const iterator pointing to first (oldest) element in buffer.
size_t size() const noexcept
Return number of elements in buffer.
T & back()
Return reference to last (newest) element in buffer.
iterator end() noexcept
Return an iterator pointing past the last (newest) element in buffer.
const_iterator end() const noexcept
Return a const iterator pointing past the last (newest) element in buffer.
ring_buffer(ring_buffer &&other)=delete
Copy constructor.
void pop_front()
Remove oldest element from buffer.
ring_buffer()
Default constructor.
iterator begin() noexcept
Return an iterator pointing to first (oldest) element in buffer.
bool operator==(ring_buffer< T > const &other) const noexcept
Equality operator.
const_iterator begin() const noexcept
Return a const iterator pointing to first (oldest) element in buffer.
ring_buffer(ring_buffer const &other)=delete
Copy constructor.
T const & front() const
Return a reference to first (oldest) element in buffer.
ring_buffer & operator=(ring_buffer const &rhs)=delete
Assignment operator.
void push_back(T const &item)
Inserts new element in buffer.
size_t capacity(void) const noexcept
Return maximum buffer size.
bool empty(void) const noexcept
Return true if buffer is empty.
SCC common utilities.
Definition bit_field.h:30