blob: 083d7cf6d10713ba764a7ece69517129c33dc1f0 [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>
Stephan Pleines5fc35072024-05-22 17:33:18 -07006#include <iterator>
7#include <new>
8#include <type_traits>
Parker Schuhecd057f2017-03-11 20:03:01 -08009
10namespace aos {
11
12// This is a helper to keep track of some amount of recent data. As you push
13// data into the ring buffer, it gets stored. If the buffer becomes full, it
14// will start overwriting the oldest data.
15template <typename Data, size_t buffer_size>
16class RingBuffer {
Austin Schuh4075ff62021-03-31 23:22:35 -070017 template <typename ValueType, typename BufferType>
18 struct generic_iterator {
19 using iterator_category = ::std::random_access_iterator_tag;
20 using value_type = ValueType;
21 using difference_type = ::std::ptrdiff_t;
22 using pointer = value_type *;
23 using reference = value_type &;
24
25 explicit generic_iterator(BufferType *buffer, size_t index)
26 : buffer_(buffer), index_(index) {}
27
28 generic_iterator &operator++() {
29 ++index_;
30 return *this;
31 }
32 generic_iterator operator++(int) {
33 const generic_iterator retval = *this;
34 ++(*this);
35 return retval;
36 }
37 generic_iterator &operator--() {
38 --index_;
39 return *this;
40 }
41 generic_iterator operator--(int) {
42 const generic_iterator retval = *this;
43 --(*this);
44 return retval;
45 }
46
47 generic_iterator &operator+=(ptrdiff_t i) {
48 index_ += i;
49 return *this;
50 }
51 generic_iterator operator+(ptrdiff_t i) const {
52 generic_iterator retval = *this;
53 retval += i;
54 return retval;
55 }
56 generic_iterator &operator-=(ptrdiff_t i) {
57 index_ -= i;
58 return *this;
59 }
60 generic_iterator operator-(ptrdiff_t i) const {
61 generic_iterator retval = *this;
62 retval -= i;
63 return retval;
64 }
65
66 ptrdiff_t operator-(generic_iterator other) const {
67 return index_ - other.index_;
68 }
69
70 bool operator==(generic_iterator other) const {
71 return buffer_ == other.buffer_ && index_ == other.index_;
72 }
73 bool operator!=(generic_iterator other) const { return !(*this == other); }
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 bool operator>=(generic_iterator other) const {
85 return buffer_ == other.buffer_ && index_ >= other.index_;
86 }
87
88 reference operator*() const { return (*buffer_)[index_]; }
89 pointer operator->() const { return &(*buffer_)[index_]; }
90
91 reference operator[](difference_type i) const {
92 return (*buffer_)[index_ + i];
93 }
94
95 private:
96 BufferType *buffer_;
97 size_t index_;
98 };
99
Parker Schuhecd057f2017-03-11 20:03:01 -0800100 public:
Austin Schuh4075ff62021-03-31 23:22:35 -0700101 using iterator = generic_iterator<Data, RingBuffer>;
102 using const_iterator = generic_iterator<const Data, const RingBuffer>;
103
Parker Schuhfea48582017-03-11 20:15:32 -0800104 static constexpr size_t kBufferSize = buffer_size;
105
James Kuszmaul97f750d2019-01-20 20:08:03 -0800106 constexpr RingBuffer() {}
Austin Schuh4075ff62021-03-31 23:22:35 -0700107 ~RingBuffer() { Reset(); }
Parker Schuhecd057f2017-03-11 20:03:01 -0800108
Austin Schuh4075ff62021-03-31 23:22:35 -0700109 // Add an item to the RingBuffer, overwriting the oldest element if necessary.
110 //
111 // Invalidates the end iterator.
112 void Push(Data data) {
Parker Schuhecd057f2017-03-11 20:03:01 -0800113 if (full()) {
Austin Schuh4075ff62021-03-31 23:22:35 -0700114 (*this)[0] = std::move(data);
Parker Schuhecd057f2017-03-11 20:03:01 -0800115 oldest_ = (oldest_ + 1) % buffer_size;
116 } else {
Austin Schuh4075ff62021-03-31 23:22:35 -0700117 new (&(*this)[size_]) Data(std::move(data));
Parker Schuhecd057f2017-03-11 20:03:01 -0800118 ++size_;
119 }
120 }
121
Austin Schuh4075ff62021-03-31 23:22:35 -0700122 // Removes the oldest element.
123 //
124 // Invalidates all iterators.
Parker Schuh208a58d2017-04-12 20:51:38 -0700125 void Shift() {
Austin Schuh4075ff62021-03-31 23:22:35 -0700126 (*this)[0].~Data();
Parker Schuh208a58d2017-04-12 20:51:38 -0700127 oldest_ = (oldest_ + 1) % buffer_size;
128 --size_;
129 }
130
Parker Schuhecd057f2017-03-11 20:03:01 -0800131 // Return the value of the index requested, adjusted so that the RingBuffer
Parker Schuh208a58d2017-04-12 20:51:38 -0700132 // contains the oldest element first and the newest last.
Parker Schuhecd057f2017-03-11 20:03:01 -0800133 Data &operator[](size_t index) {
Austin Schuh4075ff62021-03-31 23:22:35 -0700134#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
135 return *std::launder(
136 reinterpret_cast<Data *>(&data_[(oldest_ + index) % buffer_size]));
137#else
138 // TODO(brian): Remove this when all our compilers are 17 or newer.
139 return *reinterpret_cast<Data *>(&data_[(oldest_ + index) % buffer_size]);
140#endif
Parker Schuhecd057f2017-03-11 20:03:01 -0800141 }
142
143 const Data &operator[](size_t index) const {
Austin Schuh4075ff62021-03-31 23:22:35 -0700144#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
145 return *std::launder(reinterpret_cast<const Data *>(
146 &data_[(oldest_ + index) % buffer_size]));
147#else
148 // TODO(brian): Remove this when all our compilers are 17 or newer.
149 return *reinterpret_cast<const Data *>(
150 &data_[(oldest_ + index) % buffer_size]);
151#endif
Parker Schuhecd057f2017-03-11 20:03:01 -0800152 }
153
154 // Returns the capacity of the RingBuffer
155 size_t capacity() const { return buffer_size; }
156
157 // Returns the number of elements stored in the RingBuffer
158 size_t size() const { return size_; }
159
160 // Is the RingBuffer empty or full?
161 bool empty() const { return size_ == 0; }
162
163 bool full() const { return size_ == buffer_size; }
164
Austin Schuhd85c66e2017-04-16 10:50:33 -0700165 // Clears all the data out of the buffer.
Austin Schuh4075ff62021-03-31 23:22:35 -0700166 //
167 // Invalidates all iterators.
168 void Reset() {
169 while (!empty()) {
170 Shift();
Austin Schuh39f7ae92019-04-14 17:14:50 -0700171 }
Austin Schuh4075ff62021-03-31 23:22:35 -0700172 }
Austin Schuh39f7ae92019-04-14 17:14:50 -0700173
174 iterator begin() { return iterator(this, 0); }
175 iterator end() { return iterator(this, size()); }
176
177 const_iterator begin() const { return const_iterator(this, 0); }
178 const_iterator end() const { return const_iterator(this, size()); }
179
Parker Schuhecd057f2017-03-11 20:03:01 -0800180 private:
Austin Schuh4075ff62021-03-31 23:22:35 -0700181 using DataStorage =
182 typename std::aligned_storage<sizeof(Data), alignof(Data)>::type;
183 std::array<DataStorage, buffer_size> data_;
Parker Schuhecd057f2017-03-11 20:03:01 -0800184
Austin Schuh4075ff62021-03-31 23:22:35 -0700185 // Oldest contains the oldest item added to the RingBuffer which will be
186 // the next one to be overwritten
Parker Schuhecd057f2017-03-11 20:03:01 -0800187 size_t oldest_ = 0;
188 size_t size_ = 0;
189};
190
191} // namespace aos
192
John Park33858a32018-09-28 23:05:48 -0700193#endif // AOS_RING_BUFFER_H_