blob: 5e9972eef8fde6041ce33eeed4dad126cd516f93 [file] [log] [blame]
Philip Quinn9b8926e2023-01-31 14:50:02 -08001/*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
18
19#include <algorithm>
20#include <compare>
21#include <cstddef>
22#include <iterator>
23#include <memory>
24#include <type_traits>
25#include <utility>
26
Philip Quinn9b8926e2023-01-31 14:50:02 -080027#include <android-base/stringprintf.h>
28
29namespace android {
30
31// A fixed-size ring buffer of elements.
32//
33// Elements can only be removed from the front/back or added to the front/back, but with O(1)
34// performance. Elements from the opposing side are evicted when new elements are pushed onto a full
35// buffer.
36template <typename T>
37class RingBuffer {
38public:
39 using value_type = T;
40 using size_type = size_t;
41 using difference_type = ptrdiff_t;
42 using reference = value_type&;
43 using const_reference = const value_type&;
44 using pointer = value_type*;
45 using const_pointer = const value_type*;
46
47 template <typename U>
48 class Iterator;
49 using iterator = Iterator<T>;
50 using const_iterator = Iterator<const T>;
51
52 // Creates an empty ring buffer that can hold some capacity.
53 explicit RingBuffer(size_type capacity)
54 : mBuffer(std::allocator<value_type>().allocate(capacity)), mCapacity(capacity) {}
55
56 // Creates a full ring buffer holding a fixed number of elements initialised to some value.
57 explicit RingBuffer(size_type count, const_reference value) : RingBuffer(count) {
58 while (count) {
59 pushBack(value);
60 --count;
61 }
62 }
63
64 RingBuffer(const RingBuffer& other) : RingBuffer(other.capacity()) {
65 for (const auto& element : other) {
66 pushBack(element);
67 }
68 }
69
70 RingBuffer(RingBuffer&& other) noexcept { *this = std::move(other); }
71
72 ~RingBuffer() {
73 if (mBuffer) {
74 clear();
75 std::allocator<value_type>().deallocate(mBuffer, mCapacity);
76 }
77 }
78
79 RingBuffer& operator=(const RingBuffer& other) { return *this = RingBuffer(other); }
80
81 RingBuffer& operator=(RingBuffer&& other) noexcept {
82 if (this == &other) {
83 return *this;
84 }
85 if (mBuffer) {
86 clear();
87 std::allocator<value_type>().deallocate(mBuffer, mCapacity);
88 }
89 mBuffer = std::move(other.mBuffer);
90 mCapacity = other.mCapacity;
91 mBegin = other.mBegin;
92 mSize = other.mSize;
93 other.mBuffer = nullptr;
94 other.mCapacity = 0;
95 other.mBegin = 0;
96 other.mSize = 0;
97 return *this;
98 }
99
100 iterator begin() { return {*this, 0}; }
101 const_iterator begin() const { return {*this, 0}; }
102 iterator end() { return {*this, mSize}; }
103 const_iterator end() const { return {*this, mSize}; }
104
105 reference operator[](size_type i) { return mBuffer[bufferIndex(i)]; }
106 const_reference operator[](size_type i) const { return mBuffer[bufferIndex(i)]; }
107
108 // Removes all elements from the buffer.
109 void clear() {
110 std::destroy(begin(), end());
111 mSize = 0;
112 }
113
114 // Removes and returns the first element from the buffer.
115 value_type popFront() {
116 value_type element = mBuffer[mBegin];
117 std::destroy_at(std::addressof(mBuffer[mBegin]));
118 mBegin = next(mBegin);
119 --mSize;
120 return element;
121 }
122
123 // Removes and returns the last element from the buffer.
124 value_type popBack() {
125 size_type backIndex = bufferIndex(mSize - 1);
126 value_type element = mBuffer[backIndex];
127 std::destroy_at(std::addressof(mBuffer[backIndex]));
128 --mSize;
129 return element;
130 }
131
132 // Adds an element to the front of the buffer.
133 void pushFront(const value_type& element) { pushFront(value_type(element)); }
134 void pushFront(value_type&& element) {
135 mBegin = previous(mBegin);
136 if (size() == capacity()) {
137 mBuffer[mBegin] = std::forward<value_type>(element);
138 } else {
139 // The space at mBuffer[mBegin] is uninitialised.
140 // TODO: Use std::construct_at when it becomes available.
141 new (std::addressof(mBuffer[mBegin])) value_type(std::forward<value_type>(element));
142 ++mSize;
143 }
144 }
145
146 // Adds an element to the back of the buffer.
147 void pushBack(const value_type& element) { pushBack(value_type(element)); }
148 void pushBack(value_type&& element) {
149 if (size() == capacity()) {
150 mBuffer[mBegin] = std::forward<value_type>(element);
151 mBegin = next(mBegin);
152 } else {
153 // The space at mBuffer[...] is uninitialised.
154 // TODO: Use std::construct_at when it becomes available.
155 new (std::addressof(mBuffer[bufferIndex(mSize)]))
156 value_type(std::forward<value_type>(element));
157 ++mSize;
158 }
159 }
160
161 bool empty() const { return mSize == 0; }
162 size_type capacity() const { return mCapacity; }
163 size_type size() const { return mSize; }
164
165 void swap(RingBuffer& other) noexcept {
166 using std::swap;
167 swap(mBuffer, other.mBuffer);
168 swap(mCapacity, other.mCapacity);
169 swap(mBegin, other.mBegin);
170 swap(mSize, other.mSize);
171 }
172
173 friend void swap(RingBuffer& lhs, RingBuffer& rhs) noexcept { lhs.swap(rhs); }
174
175 template <typename U>
176 class Iterator {
177 private:
178 using ContainerType = std::conditional_t<std::is_const_v<U>, const RingBuffer, RingBuffer>;
179
180 public:
181 using iterator_category = std::random_access_iterator_tag;
182 using size_type = ContainerType::size_type;
183 using difference_type = ContainerType::difference_type;
184 using value_type = std::remove_cv_t<U>;
185 using pointer = U*;
186 using reference = U&;
187
188 Iterator(ContainerType& container, size_type index)
189 : mContainer(container), mIndex(index) {}
190
191 Iterator(const Iterator&) = default;
192 Iterator& operator=(const Iterator&) = default;
193
194 Iterator& operator++() {
195 ++mIndex;
196 return *this;
197 }
198
199 Iterator operator++(int) {
200 Iterator iterator(*this);
201 ++(*this);
202 return iterator;
203 }
204
205 Iterator& operator--() {
206 --mIndex;
207 return *this;
208 }
209
210 Iterator operator--(int) {
211 Iterator iterator(*this);
212 --(*this);
213 return iterator;
214 }
215
216 Iterator& operator+=(difference_type n) {
217 mIndex += n;
218 return *this;
219 }
220
221 Iterator operator+(difference_type n) {
222 Iterator iterator(*this);
223 return iterator += n;
224 }
225
226 Iterator& operator-=(difference_type n) { return *this += -n; }
227
228 Iterator operator-(difference_type n) {
229 Iterator iterator(*this);
230 return iterator -= n;
231 }
232
233 difference_type operator-(const Iterator& other) { return mIndex - other.mIndex; }
234
235 bool operator==(const Iterator& rhs) const { return mIndex == rhs.mIndex; }
236
237 bool operator!=(const Iterator& rhs) const { return !(*this == rhs); }
238
239 friend auto operator<=>(const Iterator& lhs, const Iterator& rhs) {
240 return lhs.mIndex <=> rhs.mIndex;
241 }
242
243 reference operator[](difference_type n) { return *(*this + n); }
244
245 reference operator*() const { return mContainer[mIndex]; }
246 pointer operator->() const { return std::addressof(mContainer[mIndex]); }
247
248 private:
249 ContainerType& mContainer;
250 size_type mIndex = 0;
251 };
252
253private:
254 // Returns the index of the next element in mBuffer.
255 size_type next(size_type index) const {
256 if (index == capacity() - 1) {
257 return 0;
258 } else {
259 return index + 1;
260 }
261 }
262
263 // Returns the index of the previous element in mBuffer.
264 size_type previous(size_type index) const {
265 if (index == 0) {
266 return capacity() - 1;
267 } else {
268 return index - 1;
269 }
270 }
271
272 // Converts the index of an element in [0, size()] to its corresponding index in mBuffer.
273 size_type bufferIndex(size_type elementIndex) const {
Yeabkal Wubshit64f090f2023-03-03 17:35:11 -0800274 if (elementIndex > size()) {
275 abort();
276 }
Philip Quinn9b8926e2023-01-31 14:50:02 -0800277 size_type index = mBegin + elementIndex;
278 if (index >= capacity()) {
279 index -= capacity();
280 }
Yeabkal Wubshit64f090f2023-03-03 17:35:11 -0800281 if (index >= capacity()) {
282 abort();
283 }
Philip Quinn9b8926e2023-01-31 14:50:02 -0800284 return index;
285 }
286
287 pointer mBuffer = nullptr;
288 size_type mCapacity = 0; // Total capacity of mBuffer.
289 size_type mBegin = 0; // Index of the first initialised element in mBuffer.
290 size_type mSize = 0; // Total number of initialised elements.
291};
292
293} // namespace android