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