blob: 7d0bb7e3f9ee88b64ce1644efa5fa81807c1eb8e [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>
Brian Silverman4c7235a2021-11-17 19:04:37 -08005#include <cstddef>
Parker Schuhecd057f2017-03-11 20:03:01 -08006
7namespace aos {
8
9// This is a helper to keep track of some amount of recent data. As you push
10// data into the ring buffer, it gets stored. If the buffer becomes full, it
11// will start overwriting the oldest data.
12template <typename Data, size_t buffer_size>
13class RingBuffer {
Austin Schuh4075ff62021-03-31 23:22:35 -070014 template <typename ValueType, typename BufferType>
15 struct generic_iterator {
16 using iterator_category = ::std::random_access_iterator_tag;
17 using value_type = ValueType;
18 using difference_type = ::std::ptrdiff_t;
19 using pointer = value_type *;
20 using reference = value_type &;
21
22 explicit generic_iterator(BufferType *buffer, size_t index)
23 : buffer_(buffer), index_(index) {}
24
25 generic_iterator &operator++() {
26 ++index_;
27 return *this;
28 }
29 generic_iterator operator++(int) {
30 const generic_iterator retval = *this;
31 ++(*this);
32 return retval;
33 }
34 generic_iterator &operator--() {
35 --index_;
36 return *this;
37 }
38 generic_iterator operator--(int) {
39 const generic_iterator retval = *this;
40 --(*this);
41 return retval;
42 }
43
44 generic_iterator &operator+=(ptrdiff_t i) {
45 index_ += i;
46 return *this;
47 }
48 generic_iterator operator+(ptrdiff_t i) const {
49 generic_iterator retval = *this;
50 retval += i;
51 return retval;
52 }
53 generic_iterator &operator-=(ptrdiff_t i) {
54 index_ -= i;
55 return *this;
56 }
57 generic_iterator operator-(ptrdiff_t i) const {
58 generic_iterator retval = *this;
59 retval -= i;
60 return retval;
61 }
62
63 ptrdiff_t operator-(generic_iterator other) const {
64 return index_ - other.index_;
65 }
66
67 bool operator==(generic_iterator other) const {
68 return buffer_ == other.buffer_ && index_ == other.index_;
69 }
70 bool operator!=(generic_iterator other) const { return !(*this == other); }
71
72 bool operator<(generic_iterator other) const {
73 return buffer_ == other.buffer_ && index_ < other.index_;
74 }
75 bool operator>(generic_iterator other) const {
76 return buffer_ == other.buffer_ && index_ > other.index_;
77 }
78 bool operator<=(generic_iterator other) const {
79 return buffer_ == other.buffer_ && index_ <= other.index_;
80 }
81 bool operator>=(generic_iterator other) const {
82 return buffer_ == other.buffer_ && index_ >= other.index_;
83 }
84
85 reference operator*() const { return (*buffer_)[index_]; }
86 pointer operator->() const { return &(*buffer_)[index_]; }
87
88 reference operator[](difference_type i) const {
89 return (*buffer_)[index_ + i];
90 }
91
92 private:
93 BufferType *buffer_;
94 size_t index_;
95 };
96
Parker Schuhecd057f2017-03-11 20:03:01 -080097 public:
Austin Schuh4075ff62021-03-31 23:22:35 -070098 using iterator = generic_iterator<Data, RingBuffer>;
99 using const_iterator = generic_iterator<const Data, const RingBuffer>;
100
Parker Schuhfea48582017-03-11 20:15:32 -0800101 static constexpr size_t kBufferSize = buffer_size;
102
James Kuszmaul97f750d2019-01-20 20:08:03 -0800103 constexpr RingBuffer() {}
Austin Schuh4075ff62021-03-31 23:22:35 -0700104 ~RingBuffer() { Reset(); }
Parker Schuhecd057f2017-03-11 20:03:01 -0800105
Austin Schuh4075ff62021-03-31 23:22:35 -0700106 // Add an item to the RingBuffer, overwriting the oldest element if necessary.
107 //
108 // Invalidates the end iterator.
109 void Push(Data data) {
Parker Schuhecd057f2017-03-11 20:03:01 -0800110 if (full()) {
Austin Schuh4075ff62021-03-31 23:22:35 -0700111 (*this)[0] = std::move(data);
Parker Schuhecd057f2017-03-11 20:03:01 -0800112 oldest_ = (oldest_ + 1) % buffer_size;
113 } else {
Austin Schuh4075ff62021-03-31 23:22:35 -0700114 new (&(*this)[size_]) Data(std::move(data));
Parker Schuhecd057f2017-03-11 20:03:01 -0800115 ++size_;
116 }
117 }
118
Austin Schuh4075ff62021-03-31 23:22:35 -0700119 // Removes the oldest element.
120 //
121 // Invalidates all iterators.
Parker Schuh208a58d2017-04-12 20:51:38 -0700122 void Shift() {
Austin Schuh4075ff62021-03-31 23:22:35 -0700123 (*this)[0].~Data();
Parker Schuh208a58d2017-04-12 20:51:38 -0700124 oldest_ = (oldest_ + 1) % buffer_size;
125 --size_;
126 }
127
Parker Schuhecd057f2017-03-11 20:03:01 -0800128 // Return the value of the index requested, adjusted so that the RingBuffer
Parker Schuh208a58d2017-04-12 20:51:38 -0700129 // contains the oldest element first and the newest last.
Parker Schuhecd057f2017-03-11 20:03:01 -0800130 Data &operator[](size_t index) {
Austin Schuh4075ff62021-03-31 23:22:35 -0700131#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
132 return *std::launder(
133 reinterpret_cast<Data *>(&data_[(oldest_ + index) % buffer_size]));
134#else
135 // TODO(brian): Remove this when all our compilers are 17 or newer.
136 return *reinterpret_cast<Data *>(&data_[(oldest_ + index) % buffer_size]);
137#endif
Parker Schuhecd057f2017-03-11 20:03:01 -0800138 }
139
140 const Data &operator[](size_t index) const {
Austin Schuh4075ff62021-03-31 23:22:35 -0700141#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
142 return *std::launder(reinterpret_cast<const Data *>(
143 &data_[(oldest_ + index) % buffer_size]));
144#else
145 // TODO(brian): Remove this when all our compilers are 17 or newer.
146 return *reinterpret_cast<const Data *>(
147 &data_[(oldest_ + index) % buffer_size]);
148#endif
Parker Schuhecd057f2017-03-11 20:03:01 -0800149 }
150
151 // Returns the capacity of the RingBuffer
152 size_t capacity() const { return buffer_size; }
153
154 // Returns the number of elements stored in the RingBuffer
155 size_t size() const { return size_; }
156
157 // Is the RingBuffer empty or full?
158 bool empty() const { return size_ == 0; }
159
160 bool full() const { return size_ == buffer_size; }
161
Austin Schuhd85c66e2017-04-16 10:50:33 -0700162 // Clears all the data out of the buffer.
Austin Schuh4075ff62021-03-31 23:22:35 -0700163 //
164 // Invalidates all iterators.
165 void Reset() {
166 while (!empty()) {
167 Shift();
Austin Schuh39f7ae92019-04-14 17:14:50 -0700168 }
Austin Schuh4075ff62021-03-31 23:22:35 -0700169 }
Austin Schuh39f7ae92019-04-14 17:14:50 -0700170
171 iterator begin() { return iterator(this, 0); }
172 iterator end() { return iterator(this, size()); }
173
174 const_iterator begin() const { return const_iterator(this, 0); }
175 const_iterator end() const { return const_iterator(this, size()); }
176
Parker Schuhecd057f2017-03-11 20:03:01 -0800177 private:
Austin Schuh4075ff62021-03-31 23:22:35 -0700178 using DataStorage =
179 typename std::aligned_storage<sizeof(Data), alignof(Data)>::type;
180 std::array<DataStorage, buffer_size> data_;
Parker Schuhecd057f2017-03-11 20:03:01 -0800181
Austin Schuh4075ff62021-03-31 23:22:35 -0700182 // Oldest contains the oldest item added to the RingBuffer which will be
183 // the next one to be overwritten
Parker Schuhecd057f2017-03-11 20:03:01 -0800184 size_t oldest_ = 0;
185 size_t size_ = 0;
186};
187
188} // namespace aos
189
John Park33858a32018-09-28 23:05:48 -0700190#endif // AOS_RING_BUFFER_H_