• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
30 namespace 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.
37 template <typename T>
38 class RingBuffer {
39 public:
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.
RingBuffer(size_type 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.
RingBuffer(size_type count,const_reference value)58     explicit RingBuffer(size_type count, const_reference value) : RingBuffer(count) {
59         while (count) {
60             pushBack(value);
61             --count;
62         }
63     }
64 
RingBuffer(const RingBuffer & other)65     RingBuffer(const RingBuffer& other) : RingBuffer(other.capacity()) {
66         for (const auto& element : other) {
67             pushBack(element);
68         }
69     }
70 
RingBuffer(RingBuffer && other)71     RingBuffer(RingBuffer&& other) noexcept { *this = std::move(other); }
72 
~RingBuffer()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 
begin()101     iterator begin() { return {*this, 0}; }
begin()102     const_iterator begin() const { return {*this, 0}; }
end()103     iterator end() { return {*this, mSize}; }
end()104     const_iterator end() const { return {*this, mSize}; }
105 
front()106     reference front() { return mBuffer[mBegin]; }
front()107     const_reference front() const { return mBuffer[mBegin]; }
back()108     reference back() { return mBuffer[bufferIndex(mSize - 1)]; }
back()109     const_reference back() const { return mBuffer[bufferIndex(mSize - 1)]; }
110 
111     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.
clear()115     void clear() {
116         std::destroy(begin(), end());
117         mSize = 0;
118     }
119 
120     // Removes and returns the first element from the buffer.
popFront()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.
popBack()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.
pushFront(const value_type & element)139     void pushFront(const value_type& element) { pushFront(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.
pushBack(const value_type & element)153     void pushBack(const value_type& element) { pushBack(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 
empty()167     bool empty() const { return mSize == 0; }
capacity()168     size_type capacity() const { return mCapacity; }
size()169     size_type size() const { return mSize; }
170 
swap(RingBuffer & other)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 
swap(RingBuffer & lhs,RingBuffer & rhs)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 
Iterator(ContainerType & container,size_type index)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 
259 private:
260     // Returns the index of the next element in mBuffer.
next(size_type index)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.
previous(size_type index)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.
bufferIndex(size_type elementIndex)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
299