blob: 67984b7c8085c207609cee84d74b292d85472eb4 [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
27#include <android-base/logging.h>
28#include <android-base/stringprintf.h>
29
30namespace android {
31
32// A fixed-size ring buffer of elements.
33//
34// Elements can only be removed from the front/back or added to the front/back, but with O(1)
35// performance. Elements from the opposing side are evicted when new elements are pushed onto a full
36// buffer.
37template <typename T>
38class RingBuffer {
39public:
40 using value_type = T;
41 using size_type = size_t;
42 using difference_type = ptrdiff_t;
43 using reference = value_type&;
44 using const_reference = const value_type&;
45 using pointer = value_type*;
46 using const_pointer = const value_type*;
47
48 template <typename U>
49 class Iterator;
50 using iterator = Iterator<T>;
51 using const_iterator = Iterator<const T>;
52
53 // Creates an empty ring buffer that can hold some capacity.
54 explicit RingBuffer(size_type capacity)
55 : mBuffer(std::allocator<value_type>().allocate(capacity)), mCapacity(capacity) {}
56
57 // Creates a full ring buffer holding a fixed number of elements initialised to some value.
58 explicit RingBuffer(size_type count, const_reference value) : RingBuffer(count) {
59 while (count) {
60 pushBack(value);
61 --count;
62 }
63 }
64
65 RingBuffer(const RingBuffer& other) : RingBuffer(other.capacity()) {
66 for (const auto& element : other) {
67 pushBack(element);
68 }
69 }
70
71 RingBuffer(RingBuffer&& other) noexcept { *this = std::move(other); }
72
73 ~RingBuffer() {
74 if (mBuffer) {
75 clear();
76 std::allocator<value_type>().deallocate(mBuffer, mCapacity);
77 }
78 }
79
80 RingBuffer& operator=(const RingBuffer& other) { return *this = RingBuffer(other); }
81
82 RingBuffer& operator=(RingBuffer&& other) noexcept {
83 if (this == &other) {
84 return *this;
85 }
86 if (mBuffer) {
87 clear();
88 std::allocator<value_type>().deallocate(mBuffer, mCapacity);
89 }
90 mBuffer = std::move(other.mBuffer);
91 mCapacity = other.mCapacity;
92 mBegin = other.mBegin;
93 mSize = other.mSize;
94 other.mBuffer = nullptr;
95 other.mCapacity = 0;
96 other.mBegin = 0;
97 other.mSize = 0;
98 return *this;
99 }
100
101 iterator begin() { return {*this, 0}; }
102 const_iterator begin() const { return {*this, 0}; }
103 iterator end() { return {*this, mSize}; }
104 const_iterator end() const { return {*this, mSize}; }
105
106 reference operator[](size_type i) { return mBuffer[bufferIndex(i)]; }
107 const_reference operator[](size_type i) const { return mBuffer[bufferIndex(i)]; }
108
109 // Removes all elements from the buffer.
110 void clear() {
111 std::destroy(begin(), end());
112 mSize = 0;
113 }
114
115 // Removes and returns the first element from the buffer.
116 value_type popFront() {
117 value_type element = mBuffer[mBegin];
118 std::destroy_at(std::addressof(mBuffer[mBegin]));
119 mBegin = next(mBegin);
120 --mSize;
121 return element;
122 }
123
124 // Removes and returns the last element from the buffer.
125 value_type popBack() {
126 size_type backIndex = bufferIndex(mSize - 1);
127 value_type element = mBuffer[backIndex];
128 std::destroy_at(std::addressof(mBuffer[backIndex]));
129 --mSize;
130 return element;
131 }
132
133 // Adds an element to the front of the buffer.
134 void pushFront(const value_type& element) { pushFront(value_type(element)); }
135 void pushFront(value_type&& element) {
136 mBegin = previous(mBegin);
137 if (size() == capacity()) {
138 mBuffer[mBegin] = std::forward<value_type>(element);
139 } else {
140 // The space at mBuffer[mBegin] is uninitialised.
141 // TODO: Use std::construct_at when it becomes available.
142 new (std::addressof(mBuffer[mBegin])) value_type(std::forward<value_type>(element));
143 ++mSize;
144 }
145 }
146
147 // Adds an element to the back of the buffer.
148 void pushBack(const value_type& element) { pushBack(value_type(element)); }
149 void pushBack(value_type&& element) {
150 if (size() == capacity()) {
151 mBuffer[mBegin] = std::forward<value_type>(element);
152 mBegin = next(mBegin);
153 } else {
154 // The space at mBuffer[...] is uninitialised.
155 // TODO: Use std::construct_at when it becomes available.
156 new (std::addressof(mBuffer[bufferIndex(mSize)]))
157 value_type(std::forward<value_type>(element));
158 ++mSize;
159 }
160 }
161
162 bool empty() const { return mSize == 0; }
163 size_type capacity() const { return mCapacity; }
164 size_type size() const { return mSize; }
165
166 void swap(RingBuffer& other) noexcept {
167 using std::swap;
168 swap(mBuffer, other.mBuffer);
169 swap(mCapacity, other.mCapacity);
170 swap(mBegin, other.mBegin);
171 swap(mSize, other.mSize);
172 }
173
174 friend void swap(RingBuffer& lhs, RingBuffer& rhs) noexcept { lhs.swap(rhs); }
175
176 template <typename U>
177 class Iterator {
178 private:
179 using ContainerType = std::conditional_t<std::is_const_v<U>, const RingBuffer, RingBuffer>;
180
181 public:
182 using iterator_category = std::random_access_iterator_tag;
183 using size_type = ContainerType::size_type;
184 using difference_type = ContainerType::difference_type;
185 using value_type = std::remove_cv_t<U>;
186 using pointer = U*;
187 using reference = U&;
188
189 Iterator(ContainerType& container, size_type index)
190 : mContainer(container), mIndex(index) {}
191
192 Iterator(const Iterator&) = default;
193 Iterator& operator=(const Iterator&) = default;
194
195 Iterator& operator++() {
196 ++mIndex;
197 return *this;
198 }
199
200 Iterator operator++(int) {
201 Iterator iterator(*this);
202 ++(*this);
203 return iterator;
204 }
205
206 Iterator& operator--() {
207 --mIndex;
208 return *this;
209 }
210
211 Iterator operator--(int) {
212 Iterator iterator(*this);
213 --(*this);
214 return iterator;
215 }
216
217 Iterator& operator+=(difference_type n) {
218 mIndex += n;
219 return *this;
220 }
221
222 Iterator operator+(difference_type n) {
223 Iterator iterator(*this);
224 return iterator += n;
225 }
226
227 Iterator& operator-=(difference_type n) { return *this += -n; }
228
229 Iterator operator-(difference_type n) {
230 Iterator iterator(*this);
231 return iterator -= n;
232 }
233
234 difference_type operator-(const Iterator& other) { return mIndex - other.mIndex; }
235
236 bool operator==(const Iterator& rhs) const { return mIndex == rhs.mIndex; }
237
238 bool operator!=(const Iterator& rhs) const { return !(*this == rhs); }
239
240 friend auto operator<=>(const Iterator& lhs, const Iterator& rhs) {
241 return lhs.mIndex <=> rhs.mIndex;
242 }
243
244 reference operator[](difference_type n) { return *(*this + n); }
245
246 reference operator*() const { return mContainer[mIndex]; }
247 pointer operator->() const { return std::addressof(mContainer[mIndex]); }
248
249 private:
250 ContainerType& mContainer;
251 size_type mIndex = 0;
252 };
253
254private:
255 // Returns the index of the next element in mBuffer.
256 size_type next(size_type index) const {
257 if (index == capacity() - 1) {
258 return 0;
259 } else {
260 return index + 1;
261 }
262 }
263
264 // Returns the index of the previous element in mBuffer.
265 size_type previous(size_type index) const {
266 if (index == 0) {
267 return capacity() - 1;
268 } else {
269 return index - 1;
270 }
271 }
272
273 // Converts the index of an element in [0, size()] to its corresponding index in mBuffer.
274 size_type bufferIndex(size_type elementIndex) const {
275 CHECK_LE(elementIndex, size());
276 size_type index = mBegin + elementIndex;
277 if (index >= capacity()) {
278 index -= capacity();
279 }
280 CHECK_LT(index, capacity())
281 << android::base::StringPrintf("Invalid index calculated for element (%zu) "
282 "in buffer of size %zu",
283 elementIndex, size());
284 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