MADARA  3.4.1
CircularBuffer.h
Go to the documentation of this file.
1 
2 
3 #ifndef MADARA_KNOWLEDGE_CIRCULAR_BUFFER_H_
4 #define MADARA_KNOWLEDGE_CIRCULAR_BUFFER_H_
5 
13 #include <sstream>
14 #include <memory>
15 #include <type_traits>
16 #include "madara/MadaraExport.h"
19 
20 namespace madara
21 {
22 namespace utility
23 {
30 template<typename T>
32 {
33 public:
38  CircularBuffer() = default;
39 
45  explicit CircularBuffer(size_t capacity, size_t initial_index = 0)
46  : front_(initial_index),
47  back_(initial_index),
48  data_((T*)operator new(sizeof(T) * capacity)),
49  cap_(capacity)
50  {
51  }
52 
57  : CircularBuffer(other.size(), other.front_)
58  {
59  for (const T& cur : other)
60  {
61  emplace_back(cur);
62  }
63  }
64 
68  CircularBuffer(CircularBuffer&& other) noexcept
69  {
70  swap(other);
71  }
72 
77  {
78  CircularBuffer tmp(other);
79  *this = std::move(tmp);
80  return *this;
81  }
82 
87  {
88  swap(other);
89  return *this;
90  }
91 
95  ~CircularBuffer() noexcept
96  {
97  clear();
98  operator delete(data_);
99  }
100 
104  void swap(CircularBuffer& other) noexcept
105  {
106  using std::swap;
107  swap(front_, other.front_);
108  swap(back_, other.back_);
109  swap(data_, other.data_);
110  swap(cap_, other.cap_);
111  }
112 
118  void reserve(size_t size)
119  {
120  if (size == cap_)
121  {
122  // Nothing to do
123  return;
124  }
125 
126  CircularBuffer tmp(size, front_);
127  while (!empty())
128  {
129  tmp.emplace_back(std::move(pop_front()));
130  }
131  swap(tmp);
132  }
133 
135  bool empty() const
136  {
137  return front_ == back_;
138  }
139 
141  size_t capacity() const
142  {
143  return cap_;
144  }
145 
147  size_t size() const
148  {
149  return back_ - front_;
150  }
151 
153  size_t front_index() const
154  {
155  return front_;
156  }
157 
159  size_t back_index() const
160  {
161  return back_ - 1;
162  }
163 
165  size_t actual(size_t i) const
166  {
167  return i % capacity();
168  }
169 
171  size_t front_actual() const
172  {
173  return actual(front_index());
174  }
175 
177  size_t back_actual() const
178  {
179  return actual(back_index());
180  }
181 
183  void clear()
184  {
185  while (!empty())
186  {
187  discard_front();
188  }
189  }
190 
193  {
194  public:
195  using value_type = const T;
196  using difference_type = size_t;
197  using reference = const T&;
198  using pointer = const T*;
199  using iterator_category = std::random_access_iterator_tag;
200 
202  {
203  return (*buf_)[idx_];
204  }
205  reference at() const
206  {
207  return buf_->at(idx_);
208  }
210  {
211  return &(*buf_)[idx_];
212  }
213  reference operator[](size_t i) const
214  {
215  return (*buf_)[idx_ + i];
216  }
217  reference at(size_t i) const
218  {
219  return buf_->at(idx_ + i);
220  }
222  {
223  ++idx_;
224  return *this;
225  }
227  {
228  --idx_;
229  return *this;
230  }
232  {
233  auto ret = *this;
234  ++idx_;
235  return ret;
236  }
238  {
239  auto ret = *this;
240  --idx_;
241  return ret;
242  }
243 
245  size_t index() const
246  {
247  return idx_;
248  }
249 
251  size_t index_actual() const
252  {
253  return buf_->actual(idx_);
254  }
255 
256  bool operator==(const const_iterator& other) const
257  {
258  return buf_ == other.buf_ && idx_ == other.idx_;
259  }
260 
261  bool operator!=(const const_iterator& other) const
262  {
263  return !operator==(other);
264  }
265 
266  bool operator<(const const_iterator& other) const
267  {
268  return buf_ < other.buf_ && idx_ < other.idx_;
269  }
270 
271  bool operator>(const const_iterator& other) const
272  {
273  return buf_ > other.buf_ && idx_ > other.idx_;
274  }
275 
276  bool operator<=(const const_iterator& other) const
277  {
278  return !operator>(other);
279  }
280 
281  bool operator>=(const const_iterator& other) const
282  {
283  return !operator<(other);
284  }
285 
287  {
288  idx_ += n;
289  return *this;
290  }
291 
293  {
294  idx_ -= n;
295  return *this;
296  }
297 
298  const_iterator operator+(size_t n) const
299  {
300  const_iterator ret = *this;
301  ret += n;
302  return ret;
303  }
304 
305  friend const_iterator operator+(size_t n, const const_iterator& me)
306  {
307  const_iterator ret = me;
308  ret += n;
309  return ret;
310  }
311 
312  const_iterator operator-(size_t n) const
313  {
314  const_iterator ret = *this;
315  ret -= n;
316  return ret;
317  }
318 
319  size_t operator-(const const_iterator& other) const
320  {
321  if (buf_ != other.buf_)
322  {
323  return (size_t)-1;
324  }
325 
326  return idx_ - other.idx_;
327  }
328 
329  private:
330  const_iterator(const CircularBuffer& buf, size_t idx)
331  : buf_(&buf), idx_(idx)
332  {
333  }
334 
336  size_t idx_;
337 
338  friend class CircularBuffer<T>;
339  };
340 
342  class iterator
343  {
344  public:
345  using value_type = T;
346  using difference_type = size_t;
347  using reference = T&;
348  using pointer = T*;
349  using iterator_category = std::random_access_iterator_tag;
350 
352  {
353  return (*buf_)[idx_];
354  }
355  reference at() const
356  {
357  return buf_->at(idx_);
358  }
360  {
361  return &(*buf_)[idx_];
362  }
363  reference operator[](size_t i) const
364  {
365  return (*buf_)[idx_ + i];
366  }
367  reference at(size_t i) const
368  {
369  return buf_->at(idx_ + i);
370  }
372  {
373  ++idx_;
374  return *this;
375  }
377  {
378  --idx_;
379  return *this;
380  }
382  {
383  auto ret = *this;
384  ++idx_;
385  return ret;
386  }
388  {
389  auto ret = *this;
390  --idx_;
391  return ret;
392  }
393 
395  size_t index() const
396  {
397  return idx_;
398  }
400  size_t index_actual() const
401  {
402  return buf_->actual(idx_);
403  }
404 
405  bool operator==(const iterator& other) const
406  {
407  return buf_ == other.buf_ && idx_ == other.idx_;
408  }
409 
410  bool operator!=(const iterator& other) const
411  {
412  return !operator==(other);
413  }
414 
415  bool operator<(const iterator& other) const
416  {
417  return buf_ < other.buf_ && idx_ < other.idx_;
418  }
419 
420  bool operator>(const iterator& other) const
421  {
422  return buf_ > other.buf_ && idx_ > other.idx_;
423  }
424 
425  bool operator<=(const iterator& other) const
426  {
427  return !operator>(other);
428  }
429 
430  bool operator>=(const iterator& other) const
431  {
432  return !operator<(other);
433  }
434 
435  iterator& operator+=(size_t n)
436  {
437  idx_ += n;
438  return *this;
439  }
440 
441  iterator& operator-=(size_t n)
442  {
443  idx_ -= n;
444  return *this;
445  }
446 
447  iterator operator+(size_t n) const
448  {
449  iterator ret = *this;
450  ret += n;
451  return ret;
452  }
453 
454  friend iterator operator+(size_t n, const iterator& me)
455  {
456  iterator ret = me;
457  ret += n;
458  return ret;
459  }
460 
461  iterator operator-(size_t n) const
462  {
463  iterator ret = *this;
464  ret -= n;
465  return ret;
466  }
467 
468  size_t operator-(const iterator& other) const
469  {
470  if (buf_ != other.buf_)
471  {
472  return (size_t)-1;
473  }
474 
475  return idx_ - other.idx_;
476  }
477 
478  private:
479  iterator(CircularBuffer& buf, size_t idx) : buf_(&buf), idx_(idx) {}
480 
482  size_t idx_;
483 
484  friend class CircularBuffer<T>;
485  };
486 
489  {
490  return {*this, front_};
491  }
492 
495  {
496  return {*this, back_};
497  }
498 
501  {
502  return {*this, front_};
503  }
504 
507  {
508  return {*this, back_};
509  }
510 
513  {
514  return {*this, front_};
515  }
516 
519  {
520  return {*this, back_};
521  }
522 
525  T& operator[](size_t i)
526  {
527  return data_[actual(i)];
528  }
529 
532  const T& operator[](size_t i) const
533  {
534  return data_[actual(i)];
535  }
536 
538  T& front()
539  {
540  return (*this)[front_index()];
541  }
542 
544  const T& front() const
545  {
546  return (*this)[front_index()];
547  }
548 
550  T& back()
551  {
552  return (*this)[back_index()];
553  }
554 
556  const T& back() const
557  {
558  return (*this)[back_index()];
559  }
560 
564  {
565  return empty() ? T() : front();
566  }
567 
571  {
572  return empty() ? T() : back();
573  }
574 
584  ssize_t check_range(size_t i) const
585  {
586  ssize_t ret = i - front_;
587  if (ret < 0)
588  {
589  return ret;
590  }
591  ret = i - back_;
592  if (ret > 0)
593  {
594  return ret;
595  }
596  return 0;
597  }
598 
599 private:
600  void throw_out_of_range(const char* func, size_t i) const
601  {
602  if (i < front_)
603  {
604  std::stringstream ss;
605  ss << "CircularBuffer::" << func << ": index " << i
606  << " is before oldest index " << front_;
607  throw std::out_of_range(ss.str());
608  }
609 
610  if (i >= back_)
611  {
612  std::stringstream ss;
613  ss << "CircularBuffer::" << func << ": index " << i
614  << " passes newest index " << back_;
615  throw std::out_of_range(ss.str());
616  }
617  }
618 
619 public:
624  T& at(size_t i)
625  {
626  throw_out_of_range(__func__, i);
627  return (*this)[i];
628  }
629 
634  const T& at(size_t i) const
635  {
636  throw_out_of_range(__func__, i);
637  return (*this)[i];
638  }
639 
644  {
645  front().~T();
646  ++front_;
647  }
648 
653  {
654  if (empty())
655  {
656  return T{};
657  }
658  T ret = std::move(front());
659  discard_front();
660  return ret;
661  }
662 
667  {
668  back().~T();
669  --back_;
670  }
671 
676  {
677  if (empty())
678  {
679  return T{};
680  }
681  T ret = std::move(back());
682  discard_back();
683  return ret;
684  }
685 
692  template<typename... Args>
693  T& emplace_back(Args&&... args)
694  {
695  if (size() >= capacity())
696  {
697  discard_front();
698  }
699  ++back_;
700  T& ret = *new (&(back())) T(std::forward<Args>(args)...);
701  return ret;
702  }
703 
710  T& push_back(const T& val)
711  {
712  return emplace_back(val);
713  }
714 
721  T& push_back(T&& val)
722  {
723  return emplace_back(std::move(val));
724  }
725 
726 private:
727  size_t front_ = 0;
728  size_t back_ = 0;
729 
730  T* data_ = nullptr;
731 
732  size_t cap_ = 0;
733 
734  friend class iterator;
735  friend class const_iterator;
736 };
737 }
738 }
739 
740 #endif // _MADARA_KNOWLEDGE_CIRCULAR_BUFFER_H_
Const input iterator for elements of a CircularBuffer. Usual semantics.
const_iterator operator+(size_t n) const
std::random_access_iterator_tag iterator_category
bool operator>=(const const_iterator &other) const
bool operator>(const const_iterator &other) const
bool operator==(const const_iterator &other) const
bool operator<=(const const_iterator &other) const
friend const_iterator operator+(size_t n, const const_iterator &me)
size_t index_actual() const
Actual index within buffer this iterator refers to.
bool operator!=(const const_iterator &other) const
size_t index() const
Index this iterator refers to.
size_t operator-(const const_iterator &other) const
const_iterator operator-(size_t n) const
const_iterator(const CircularBuffer &buf, size_t idx)
bool operator<(const const_iterator &other) const
Mutable input iterator for elements of a CircularBuffer. Usual semantics.
std::random_access_iterator_tag iterator_category
bool operator==(const iterator &other) const
size_t operator-(const iterator &other) const
iterator(CircularBuffer &buf, size_t idx)
bool operator>=(const iterator &other) const
bool operator!=(const iterator &other) const
bool operator<(const iterator &other) const
size_t index_actual() const
Actual index within buffer this iterator refers to.
bool operator<=(const iterator &other) const
size_t index() const
Index this iterator refers to.
friend iterator operator+(size_t n, const iterator &me)
bool operator>(const iterator &other) const
General purpose circular buffer container.
void clear()
Empty the buffer, destructing all elements.
T get_front()
Get a copy of the front element, or a default constructed value if the buffer is empty.
void reserve(size_t size)
Change the capacity of this CircularBuffer.
CircularBuffer(const CircularBuffer &other)
Copy constructor.
~CircularBuffer() noexcept
Destructor.
void throw_out_of_range(const char *func, size_t i) const
iterator end()
Create a mutable iterator to the back of the buffer.
T & push_back(T &&val)
Move the value val to the back of the buffer.
bool empty() const
Is this CircularBuffer empty?
void discard_front()
Remove the front element and discard it.
const_iterator cbegin() const
Create a const iterator to the back of the buffer.
T pop_front()
Remove the front element and return it.
size_t size() const
Number of elements this buffer currently holds, up to capacity.
size_t front_index() const
Index of front element.
const T & front() const
Get a const reference to the front element. Undefined behavior if empty.
T & front()
Get a reference to the front element. Undefined behavior if empty.
size_t actual(size_t i) const
Convert an actual index, to the modulused actual index within buffer.
const T & back() const
Get a const reference to the back element. Undefined behavior if empty.
size_t front_actual() const
Actual index of front within the buffer.
T pop_back()
Remove the back element and return it.
size_t back_actual() const
Actual index of back within the buffer.
size_t capacity() const
Number of elements this buffer can hold without dropping any.
void swap(CircularBuffer &other) noexcept
Swaps contents with another CircularBuffer, without copying elements.
CircularBuffer(size_t capacity, size_t initial_index=0)
Initialize with given capacity.
CircularBuffer & operator=(const CircularBuffer &other)
Copy assignment operator.
const_iterator cend() const
Create a const iterator to the back of the buffer.
T & back()
Get a reference to the back element. Undefined behavior if empty.
const T & operator[](size_t i) const
Get a const reference to the ith element.
T & at(size_t i)
Get a reference to ith element.
const_iterator begin() const
Create a const iterator to the front of the buffer.
T get_back()
Get a copy of the front element, or a default constructed value if the buffer is empty.
CircularBuffer(CircularBuffer &&other) noexcept
Move constructor.
void discard_back()
Remove the back element and discard it.
T & emplace_back(Args &&... args)
Construct a new element in-place in the back of the buffer.
T & push_back(const T &val)
Copy the value val to the back of the buffer.
T & operator[](size_t i)
Get a reference to the ith element.
CircularBuffer()=default
Construct with zero capacity.
ssize_t check_range(size_t i) const
Compares given index relative to existing range of elements (front_index() to back_index())
const_iterator end() const
Create a const iterator to the back of the buffer.
iterator begin()
Create a mutable iterator to the front of the buffer.
size_t back_index() const
Index of back element.
const T & at(size_t i) const
Get a const reference to ith element.
CircularBuffer & operator=(CircularBuffer &&other) noexcept
Move assignment operator.
Provides utility functions and classes for common tasks and needs.
Definition: IteratorImpl.h:15
Copyright(c) 2020 Galois.