blob: d2747d6fadd73a52aad188831c24a66352ea5fd6 [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
Cody Heiner18838bc2023-04-05 16:52:27 -0700105 reference front() { return mBuffer[mBegin]; }
106 const_reference front() const { return mBuffer[mBegin]; }
107 reference back() { return mBuffer[bufferIndex(mSize - 1)]; }
108 const_reference back() const { return mBuffer[bufferIndex(mSize - 1)]; }
109
Philip Quinn9b8926e2023-01-31 14:50:02 -0800110 reference operator[](size_type i) { return mBuffer[bufferIndex(i)]; }
111 const_reference operator[](size_type i) const { return mBuffer[bufferIndex(i)]; }
112
113 // Removes all elements from the buffer.
114 void clear() {
115 std::destroy(begin(), end());
116 mSize = 0;
117 }
118
119 // Removes and returns the first element from the buffer.
120 value_type popFront() {
121 value_type element = mBuffer[mBegin];
122 std::destroy_at(std::addressof(mBuffer[mBegin]));
123 mBegin = next(mBegin);
124 --mSize;
125 return element;
126 }
127
128 // Removes and returns the last element from the buffer.
129 value_type popBack() {
130 size_type backIndex = bufferIndex(mSize - 1);
131 value_type element = mBuffer[backIndex];
132 std::destroy_at(std::addressof(mBuffer[backIndex]));
133 --mSize;
134 return element;
135 }
136
137 // Adds an element to the front of the buffer.
138 void pushFront(const value_type& element) { pushFront(value_type(element)); }
139 void pushFront(value_type&& element) {
140 mBegin = previous(mBegin);
141 if (size() == capacity()) {
142 mBuffer[mBegin] = std::forward<value_type>(element);
143 } else {
144 // The space at mBuffer[mBegin] is uninitialised.
145 // TODO: Use std::construct_at when it becomes available.
146 new (std::addressof(mBuffer[mBegin])) value_type(std::forward<value_type>(element));
147 ++mSize;
148 }
149 }
150
151 // Adds an element to the back of the buffer.
152 void pushBack(const value_type& element) { pushBack(value_type(element)); }
153 void pushBack(value_type&& element) {
154 if (size() == capacity()) {
155 mBuffer[mBegin] = std::forward<value_type>(element);
156 mBegin = next(mBegin);
157 } else {
158 // The space at mBuffer[...] is uninitialised.
159 // TODO: Use std::construct_at when it becomes available.
160 new (std::addressof(mBuffer[bufferIndex(mSize)]))
161 value_type(std::forward<value_type>(element));
162 ++mSize;
163 }
164 }
165
166 bool empty() const { return mSize == 0; }
167 size_type capacity() const { return mCapacity; }
168 size_type size() const { return mSize; }
169
170 void swap(RingBuffer& other) noexcept {
171 using std::swap;
172 swap(mBuffer, other.mBuffer);
173 swap(mCapacity, other.mCapacity);
174 swap(mBegin, other.mBegin);
175 swap(mSize, other.mSize);
176 }
177
178 friend void swap(RingBuffer& lhs, RingBuffer& rhs) noexcept { lhs.swap(rhs); }
179
180 template <typename U>
181 class Iterator {
182 private:
183 using ContainerType = std::conditional_t<std::is_const_v<U>, const RingBuffer, RingBuffer>;
184
185 public:
186 using iterator_category = std::random_access_iterator_tag;
187 using size_type = ContainerType::size_type;
188 using difference_type = ContainerType::difference_type;
189 using value_type = std::remove_cv_t<U>;
190 using pointer = U*;
191 using reference = U&;
192
193 Iterator(ContainerType& container, size_type index)
194 : mContainer(container), mIndex(index) {}
195
196 Iterator(const Iterator&) = default;
197 Iterator& operator=(const Iterator&) = default;
198
199 Iterator& operator++() {
200 ++mIndex;
201 return *this;
202 }
203
204 Iterator operator++(int) {
205 Iterator iterator(*this);
206 ++(*this);
207 return iterator;
208 }
209
210 Iterator& operator--() {
211 --mIndex;
212 return *this;
213 }
214
215 Iterator operator--(int) {
216 Iterator iterator(*this);
217 --(*this);
218 return iterator;
219 }
220
221 Iterator& operator+=(difference_type n) {
222 mIndex += n;
223 return *this;
224 }
225
226 Iterator operator+(difference_type n) {
227 Iterator iterator(*this);
228 return iterator += n;
229 }
230
231 Iterator& operator-=(difference_type n) { return *this += -n; }
232
233 Iterator operator-(difference_type n) {
234 Iterator iterator(*this);
235 return iterator -= n;
236 }
237
238 difference_type operator-(const Iterator& other) { return mIndex - other.mIndex; }
239
240 bool operator==(const Iterator& rhs) const { return mIndex == rhs.mIndex; }
241
242 bool operator!=(const Iterator& rhs) const { return !(*this == rhs); }
243
244 friend auto operator<=>(const Iterator& lhs, const Iterator& rhs) {
245 return lhs.mIndex <=> rhs.mIndex;
246 }
247
248 reference operator[](difference_type n) { return *(*this + n); }
249
250 reference operator*() const { return mContainer[mIndex]; }
251 pointer operator->() const { return std::addressof(mContainer[mIndex]); }
252
253 private:
254 ContainerType& mContainer;
255 size_type mIndex = 0;
256 };
257
258private:
259 // Returns the index of the next element in mBuffer.
260 size_type next(size_type index) const {
261 if (index == capacity() - 1) {
262 return 0;
263 } else {
264 return index + 1;
265 }
266 }
267
268 // Returns the index of the previous element in mBuffer.
269 size_type previous(size_type index) const {
270 if (index == 0) {
271 return capacity() - 1;
272 } else {
273 return index - 1;
274 }
275 }
276
277 // Converts the index of an element in [0, size()] to its corresponding index in mBuffer.
278 size_type bufferIndex(size_type elementIndex) const {
Yeabkal Wubshit64f090f2023-03-03 17:35:11 -0800279 if (elementIndex > size()) {
280 abort();
281 }
Philip Quinn9b8926e2023-01-31 14:50:02 -0800282 size_type index = mBegin + elementIndex;
283 if (index >= capacity()) {
284 index -= capacity();
285 }
Yeabkal Wubshit64f090f2023-03-03 17:35:11 -0800286 if (index >= capacity()) {
287 abort();
288 }
Philip Quinn9b8926e2023-01-31 14:50:02 -0800289 return index;
290 }
291
292 pointer mBuffer = nullptr;
293 size_type mCapacity = 0; // Total capacity of mBuffer.
294 size_type mBegin = 0; // Index of the first initialised element in mBuffer.
295 size_type mSize = 0; // Total number of initialised elements.
296};
297
298} // namespace android