blob: b92835473e0d82083aafbeff76d9d0f589c064d4 [file] [log] [blame]
John Park33858a32018-09-28 23:05:48 -07001#ifndef AOS_RING_BUFFER_H_
2#define AOS_RING_BUFFER_H_
Parker Schuhecd057f2017-03-11 20:03:01 -08003
4#include <array>
5
6namespace aos {
7
8// This is a helper to keep track of some amount of recent data. As you push
9// data into the ring buffer, it gets stored. If the buffer becomes full, it
10// will start overwriting the oldest data.
11template <typename Data, size_t buffer_size>
12class RingBuffer {
Austin Schuh4075ff62021-03-31 23:22:35 -070013 template <typename ValueType, typename BufferType>
14 struct generic_iterator {
15 using iterator_category = ::std::random_access_iterator_tag;
16 using value_type = ValueType;
17 using difference_type = ::std::ptrdiff_t;
18 using pointer = value_type *;
19 using reference = value_type &;
20
21 explicit generic_iterator(BufferType *buffer, size_t index)
22 : buffer_(buffer), index_(index) {}
23
24 generic_iterator &operator++() {
25 ++index_;
26 return *this;
27 }
28 generic_iterator operator++(int) {
29 const generic_iterator retval = *this;
30 ++(*this);
31 return retval;
32 }
33 generic_iterator &operator--() {
34 --index_;
35 return *this;
36 }
37 generic_iterator operator--(int) {
38 const generic_iterator retval = *this;
39 --(*this);
40 return retval;
41 }
42
43 generic_iterator &operator+=(ptrdiff_t i) {
44 index_ += i;
45 return *this;
46 }
47 generic_iterator operator+(ptrdiff_t i) const {
48 generic_iterator retval = *this;
49 retval += i;
50 return retval;
51 }
52 generic_iterator &operator-=(ptrdiff_t i) {
53 index_ -= i;
54 return *this;
55 }
56 generic_iterator operator-(ptrdiff_t i) const {
57 generic_iterator retval = *this;
58 retval -= i;
59 return retval;
60 }
61
62 ptrdiff_t operator-(generic_iterator other) const {
63 return index_ - other.index_;
64 }
65
66 bool operator==(generic_iterator other) const {
67 return buffer_ == other.buffer_ && index_ == other.index_;
68 }
69 bool operator!=(generic_iterator other) const { return !(*this == other); }
70
71 bool operator<(generic_iterator other) const {
72 return buffer_ == other.buffer_ && index_ < other.index_;
73 }
74 bool operator>(generic_iterator other) const {
75 return buffer_ == other.buffer_ && index_ > other.index_;
76 }
77 bool operator<=(generic_iterator other) const {
78 return buffer_ == other.buffer_ && index_ <= other.index_;
79 }
80 bool operator>=(generic_iterator other) const {
81 return buffer_ == other.buffer_ && index_ >= other.index_;
82 }
83
84 reference operator*() const { return (*buffer_)[index_]; }
85 pointer operator->() const { return &(*buffer_)[index_]; }
86
87 reference operator[](difference_type i) const {
88 return (*buffer_)[index_ + i];
89 }
90
91 private:
92 BufferType *buffer_;
93 size_t index_;
94 };
95
Parker Schuhecd057f2017-03-11 20:03:01 -080096 public:
Austin Schuh4075ff62021-03-31 23:22:35 -070097 using iterator = generic_iterator<Data, RingBuffer>;
98 using const_iterator = generic_iterator<const Data, const RingBuffer>;
99
Parker Schuhfea48582017-03-11 20:15:32 -0800100 static constexpr size_t kBufferSize = buffer_size;
101
James Kuszmaul97f750d2019-01-20 20:08:03 -0800102 constexpr RingBuffer() {}
Austin Schuh4075ff62021-03-31 23:22:35 -0700103 ~RingBuffer() { Reset(); }
Parker Schuhecd057f2017-03-11 20:03:01 -0800104
Austin Schuh4075ff62021-03-31 23:22:35 -0700105 // Add an item to the RingBuffer, overwriting the oldest element if necessary.
106 //
107 // Invalidates the end iterator.
108 void Push(Data data) {
Parker Schuhecd057f2017-03-11 20:03:01 -0800109 if (full()) {
Austin Schuh4075ff62021-03-31 23:22:35 -0700110 (*this)[0] = std::move(data);
Parker Schuhecd057f2017-03-11 20:03:01 -0800111 oldest_ = (oldest_ + 1) % buffer_size;
112 } else {
Austin Schuh4075ff62021-03-31 23:22:35 -0700113 new (&(*this)[size_]) Data(std::move(data));
Parker Schuhecd057f2017-03-11 20:03:01 -0800114 ++size_;
115 }
116 }
117
Austin Schuh4075ff62021-03-31 23:22:35 -0700118 // Removes the oldest element.
119 //
120 // Invalidates all iterators.
Parker Schuh208a58d2017-04-12 20:51:38 -0700121 void Shift() {
Austin Schuh4075ff62021-03-31 23:22:35 -0700122 (*this)[0].~Data();
Parker Schuh208a58d2017-04-12 20:51:38 -0700123 oldest_ = (oldest_ + 1) % buffer_size;
124 --size_;
125 }
126
Parker Schuhecd057f2017-03-11 20:03:01 -0800127 // Return the value of the index requested, adjusted so that the RingBuffer
Parker Schuh208a58d2017-04-12 20:51:38 -0700128 // contains the oldest element first and the newest last.
Parker Schuhecd057f2017-03-11 20:03:01 -0800129 Data &operator[](size_t index) {
Austin Schuh4075ff62021-03-31 23:22:35 -0700130#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
131 return *std::launder(
132 reinterpret_cast<Data *>(&data_[(oldest_ + index) % buffer_size]));
133#else
134 // TODO(brian): Remove this when all our compilers are 17 or newer.
135 return *reinterpret_cast<Data *>(&data_[(oldest_ + index) % buffer_size]);
136#endif
Parker Schuhecd057f2017-03-11 20:03:01 -0800137 }
138
139 const Data &operator[](size_t index) const {
Austin Schuh4075ff62021-03-31 23:22:35 -0700140#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
141 return *std::launder(reinterpret_cast<const Data *>(
142 &data_[(oldest_ + index) % buffer_size]));
143#else
144 // TODO(brian): Remove this when all our compilers are 17 or newer.
145 return *reinterpret_cast<const Data *>(
146 &data_[(oldest_ + index) % buffer_size]);
147#endif
Parker Schuhecd057f2017-03-11 20:03:01 -0800148 }
149
150 // Returns the capacity of the RingBuffer
151 size_t capacity() const { return buffer_size; }
152
153 // Returns the number of elements stored in the RingBuffer
154 size_t size() const { return size_; }
155
156 // Is the RingBuffer empty or full?
157 bool empty() const { return size_ == 0; }
158
159 bool full() const { return size_ == buffer_size; }
160
Austin Schuhd85c66e2017-04-16 10:50:33 -0700161 // Clears all the data out of the buffer.
Austin Schuh4075ff62021-03-31 23:22:35 -0700162 //
163 // Invalidates all iterators.
164 void Reset() {
165 while (!empty()) {
166 Shift();
Austin Schuh39f7ae92019-04-14 17:14:50 -0700167 }
Austin Schuh4075ff62021-03-31 23:22:35 -0700168 }
Austin Schuh39f7ae92019-04-14 17:14:50 -0700169
170 iterator begin() { return iterator(this, 0); }
171 iterator end() { return iterator(this, size()); }
172
173 const_iterator begin() const { return const_iterator(this, 0); }
174 const_iterator end() const { return const_iterator(this, size()); }
175
Parker Schuhecd057f2017-03-11 20:03:01 -0800176 private:
Austin Schuh4075ff62021-03-31 23:22:35 -0700177 using DataStorage =
178 typename std::aligned_storage<sizeof(Data), alignof(Data)>::type;
179 std::array<DataStorage, buffer_size> data_;
Parker Schuhecd057f2017-03-11 20:03:01 -0800180
Austin Schuh4075ff62021-03-31 23:22:35 -0700181 // Oldest contains the oldest item added to the RingBuffer which will be
182 // the next one to be overwritten
Parker Schuhecd057f2017-03-11 20:03:01 -0800183 size_t oldest_ = 0;
184 size_t size_ = 0;
185};
186
187} // namespace aos
188
John Park33858a32018-09-28 23:05:48 -0700189#endif // AOS_RING_BUFFER_H_