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