scc 2026.07
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& operator=(ring_buffer const& rhs) = delete;
200
201 ring_buffer& operator=(ring_buffer&& rhs) = delete;
202
204 bool operator==(ring_buffer<T> const& other) const noexcept {
205 if(cap == other.cap && sz == other.sz) {
206 for(size_t i = 0; i < sz; i++) {
207 size_t idx1 = (front_idx + i) % cap;
208 size_t idx2 = (other.front_idx + i) % other.cap;
209 if(buf[idx1] != other.buf[idx2])
210 return false;
211 }
212 return true;
213 }
214 return false;
215 }
216
218 bool operator!=(ring_buffer<T> const& other) const noexcept { return !operator==(other); }
219
221 operator std::vector<T>() const {
222 std::vector<T> v;
223 v.reserve(sz);
224 std::copy(std::begin(*this), std::end(*this), std::back_inserter(v));
225 return v;
226 }
227
229 void push_back(T const& item) {
230 if(!cap)
231 throw std::length_error("ring_buffer: trying to add an element to a buffer of size 0");
232 if(sz && back_idx == front_idx)
233 throw std::overflow_error("ring_buffer: overflow error");
234 sz++;
235 buf[back_idx] = item;
236 back_idx = (back_idx + 1) % cap;
237 }
238
239 void push_back(T&& item) {
240 if(!cap)
241 throw std::length_error("ring_buffer: trying to add an element to a buffer of size 0");
242 if(sz && back_idx == front_idx)
243 throw std::overflow_error("ring_buffer: overflow error");
244 sz++;
245 buf[back_idx] = std::move(item);
246 back_idx = (back_idx + 1) % cap;
247 }
248
250 iterator begin() noexcept { return iterator(this, front_idx); }
251
253 const_iterator begin() const noexcept { return const_iterator(this, front_idx); }
254
256 const_iterator cbegin() const noexcept { return const_iterator(this, front_idx); }
257
259 iterator end() noexcept { return iterator(this, (size_t)(-1)); }
260
262 const_iterator end() const noexcept { return const_iterator(this, (size_t)(-1)); }
263
265 const_iterator cend() const noexcept { return const_iterator(this, (size_t)(-1)); }
266
268 void pop_front() {
269 if(!sz)
270 throw std::underflow_error("ring_buffer: buffer is empty");
271 front_idx = (front_idx + 1) % cap;
272 sz--;
273 }
274
276 T& front() {
277 if(!sz)
278 throw std::underflow_error("ring_buffer: buffer is empty");
279 return buf[front_idx];
280 }
281
283 T const& front() const {
284 if(!sz)
285 throw std::underflow_error("ring_buffer: buffer is empty");
286 return buf[front_idx];
287 }
288
290 T& back() {
291 if(!sz)
292 throw std::underflow_error("ring_buffer: buffer is empty");
293 return buf[(back_idx + cap - 1) % cap];
294 }
295
297 T const& back() const {
298 if(!sz)
299 throw std::underflow_error("ring_buffer: buffer is empty");
300 return buf[(back_idx + cap - 1) % cap];
301 }
302
304 void clear(void) {
305 for(size_t i = 0; i < sz; i++) {
306 buf[(front_idx + i) % cap].~T();
307 }
308
309 front_idx = back_idx;
310 sz = 0;
311 }
312
314 bool empty(void) const noexcept { return (sz == 0); }
315
317 bool full(void) const noexcept { return (sz == cap); }
318
320 size_t capacity(void) const noexcept { return cap; };
321
323 void resize(size_t new_cap) {
324 if(new_cap < sz)
325 throw std::bad_array_new_length();
326 std::unique_ptr<T[]> newbuf(new_cap ? new T[new_cap] : nullptr);
327 if(new_cap) {
328 for(size_t i = 0; i < sz; i++) {
329 newbuf[i] = std::move(buf[(front_idx + i) % cap]);
330 }
331 } else
332 sz = 0;
333 cap = new_cap;
334 buf.swap(newbuf);
335 front_idx = 0;
336 back_idx = cap ? (front_idx + sz) % cap : 0;
337 }
338
340 size_t size() const noexcept { return sz; }
341
342private:
344 size_t increment(size_t pos) const noexcept {
345 if(cap && pos != (size_t)-1)
346 pos = (pos + 1) % cap;
347 if(pos == back_idx)
348 pos = (size_t)-1;
349 return pos;
350 }
351
353 size_t decrement(size_t pos) const noexcept {
354 if(cap) {
355 if(pos == (size_t)-1)
356 pos = (back_idx + cap - 1) % cap;
357 else if(pos != front_idx)
358 pos = (pos + cap - 1) % cap;
359 }
360 return pos;
361 }
362
364 size_t add(size_t oldpos, size_t delta) const noexcept {
365 if(cap && oldpos != -1) {
366 size_t np = oldpos + (delta % cap);
367 if(np >= cap && np >= back_idx + cap)
368 np = (size_t)(-1);
369 else
370 np %= cap;
371 return np;
372 }
373 return oldpos;
374 }
375
377 size_t subtract(size_t oldpos, size_t delta) const noexcept {
378 if(cap) {
379 size_t np = (oldpos == (size_t)-1 ? back_idx : oldpos) - (delta % cap) + cap;
380 if(np < front_idx)
381 np = front_idx;
382 else
383 np %= cap;
384 return np;
385 }
386 return oldpos;
387 }
388
389 std::unique_ptr<T[]> buf;
390 size_t front_idx, back_idx, cap, sz;
391};
392
393} // 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.
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