1 /** 2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef PANDA_LIBPANDABASE_UTILS_RING_BUFFER_H 17 #define PANDA_LIBPANDABASE_UTILS_RING_BUFFER_H 18 19 #include <array> 20 #include <limits> 21 22 #include "macros.h" 23 24 namespace ark { 25 template <class T, size_t N> 26 class RingBuffer; 27 28 template <class T, size_t N, bool IS_CONST = false> 29 class RingBufferIterator { 30 public: 31 // NOLINTBEGIN(readability-identifier-naming) 32 using iterator_category = std::bidirectional_iterator_tag; 33 using value_type = std::conditional_t<IS_CONST, const T, T>; 34 using difference_type = ptrdiff_t; 35 using pointer = value_type *; 36 using reference = value_type &; 37 // NOLINTEND(readability-identifier-naming) 38 39 constexpr RingBufferIterator() noexcept = default; 40 constexpr RingBufferIterator(const RingBufferIterator &other) noexcept = default; 41 constexpr RingBufferIterator(RingBufferIterator &&other) noexcept = default; 42 43 constexpr RingBufferIterator &operator=(const RingBufferIterator &other) = default; 44 constexpr RingBufferIterator &operator=(RingBufferIterator &&other) = default; 45 46 ~RingBufferIterator() = default; 47 48 constexpr RingBufferIterator &operator++() noexcept 49 { 50 IncrementIndex(); 51 return *this; 52 } 53 54 constexpr RingBufferIterator operator++(int) noexcept // NOLINT(cert-dcl21-cpp) 55 { 56 auto tmp = RingBufferIterator(*this); 57 IncrementIndex(); 58 return tmp; 59 } 60 61 constexpr RingBufferIterator &operator--() noexcept 62 { 63 DecrementIndex(); 64 return *this; 65 } 66 67 constexpr RingBufferIterator operator--(int) noexcept // NOLINT(cert-dcl21-cpp) 68 { 69 auto tmp = RingBufferIterator(*this); 70 DecrementIndex(); 71 return tmp; 72 } 73 74 constexpr bool operator==(const RingBufferIterator &other) const noexcept 75 { 76 return index_ == other.index_; 77 } 78 79 constexpr bool operator!=(const RingBufferIterator &other) const noexcept 80 { 81 return index_ != other.index_; 82 } 83 84 constexpr reference operator*() const noexcept 85 { 86 return buffer_->operator[](index_); 87 } 88 89 constexpr pointer operator->() const noexcept 90 { 91 return static_cast<pointer>(&buffer_->operator[](index_)); 92 } 93 94 private: 95 using ArrayPtr = std::conditional_t<IS_CONST, const std::array<T, N + 1> *, std::array<T, N + 1> *>; 96 RingBufferIterator(ArrayPtr buffer,size_t index)97 constexpr RingBufferIterator(ArrayPtr buffer, size_t index) : buffer_(buffer), index_(index) {} 98 IncrementIndex()99 constexpr void IncrementIndex() noexcept 100 { 101 index_ = (index_ == N) ? 0U : index_ + 1; 102 } 103 DecrementIndex()104 constexpr void DecrementIndex() noexcept 105 { 106 index_ = (index_ == 0U) ? N : index_ - 1; 107 } 108 109 friend class RingBuffer<T, N>; 110 111 ArrayPtr buffer_ = nullptr; 112 size_t index_ = 0U; 113 }; 114 115 /** 116 * Static circular buffer in STL-style 117 * @tparam T type of values in buffer 118 * @tparam N maximum count of elements in buffer 119 */ 120 template <class T, size_t N> 121 class RingBuffer { 122 public: 123 static_assert(N > 0U, "0 is invalid size for ring buffer"); 124 125 // NOLINTBEGIN(readability-identifier-naming) 126 using value_type = T; 127 using pointer = value_type *; 128 using const_pointer = const value_type *; 129 using reference = value_type &; 130 using const_reference = const value_type &; 131 using size_type = size_t; 132 133 using iterator = RingBufferIterator<value_type, N>; 134 using const_iterator = RingBufferIterator<value_type, N, true>; 135 using reverse_iterator = std::reverse_iterator<iterator>; 136 using const_reverse_iterator = std::reverse_iterator<const_iterator>; 137 // NOLINTEND(readability-identifier-naming) 138 139 constexpr RingBuffer() = default; 140 141 constexpr RingBuffer(const RingBuffer &other) = default; 142 constexpr RingBuffer(RingBuffer &&other) = default; 143 constexpr RingBuffer &operator=(const RingBuffer &other) = default; 144 constexpr RingBuffer &operator=(RingBuffer &&other) = default; 145 146 ~RingBuffer() = default; 147 148 /** 149 * Appends the given element value to the end of the ring buffer 150 * @param value the value of the element to append 151 */ push_back(const value_type & value)152 constexpr void push_back(const value_type &value) // NOLINT(readability-identifier-naming) 153 { 154 IncrementTail(); 155 buffer_[tail_] = value; 156 } 157 158 /** 159 * Moves the given element value to the end of the ring buffer 160 * @param value the value of the element to append 161 */ push_back(value_type && value)162 constexpr void push_back(value_type &&value) // NOLINT(readability-identifier-naming) 163 { 164 emplace_back(std::move(value)); 165 } 166 167 /** 168 * Appends a new element to the end of the ring buffer 169 * 170 * @tparam Args arguments types for elemnt construction 171 * @param args arguments to forward to the constructor of the element 172 * @return a reference to the inserted element 173 */ 174 template <class... Args> emplace_back(Args...args)175 constexpr reference emplace_back(Args... args) // NOLINT(readability-identifier-naming) 176 { 177 IncrementTail(); 178 buffer_[tail_] = value_type(std::forward<Args>(args)...); 179 return buffer_[tail_]; 180 } 181 182 /** 183 * Appends the given element value to the begin of the ring buffer 184 * @param value the value of the element to append 185 */ push_front(const value_type & value)186 constexpr void push_front(const value_type &value) // NOLINT(readability-identifier-naming) 187 { 188 DecrementHead(); 189 buffer_[head_] = value; 190 } 191 192 /** 193 * Moves the given element value to the begin of the ring buffer 194 * @param value the value of the element to append 195 */ push_front(value_type && value)196 constexpr void push_front(value_type &&value) // NOLINT(readability-identifier-naming) 197 { 198 emplace_front(std::move(value)); 199 } 200 201 /** 202 * Appends a new element to the begin of the ring buffer 203 * 204 * @tparam Args arguments types for elemnt construction 205 * @param args arguments to forward to the constructor of the element 206 * @return a reference to the inserted element 207 */ 208 template <class... Args> emplace_front(Args...args)209 constexpr reference emplace_front(Args... args) // NOLINT(readability-identifier-naming) 210 { 211 DecrementHead(); 212 buffer_[head_] = value_type(std::forward<Args>(args)...); 213 return buffer_[head_]; 214 } 215 216 /// Removes the last element of the ring buffer pop_back()217 constexpr void pop_back() // NOLINT(readability-identifier-naming) 218 { 219 if constexpr (std::is_class_v<T>) { // NOLINT(readability-braces-around-statements) 220 buffer_[tail_].~value_type(); 221 } 222 DecrementBufferIndex(tail_); 223 --currentSize_; 224 } 225 226 /// Removes the first element of the ring buffer pop_front()227 constexpr void pop_front() // NOLINT(readability-identifier-naming) 228 { 229 if constexpr (std::is_class_v<T>) { // NOLINT(readability-braces-around-statements) 230 buffer_[head_].~value_type(); 231 } 232 IncrementBufferIndex(head_); 233 --currentSize_; 234 } 235 236 /// @return iterator to the first element begin()237 constexpr iterator begin() noexcept // NOLINT(readability-identifier-naming) 238 { 239 return iterator(&buffer_, head_); 240 } 241 242 /// @return a reverse iterator to the first element of the revesred ring buffer rbegin()243 constexpr reverse_iterator rbegin() noexcept // NOLINT(readability-identifier-naming) 244 { 245 return std::make_reverse_iterator(end()); 246 } 247 248 /// @return const context iterator to the first element begin()249 constexpr const_iterator begin() const noexcept // NOLINT(readability-identifier-naming) 250 { 251 return const_iterator(&buffer_, head_); 252 } 253 254 /// @return a const context reverse iterator to the first element of the reversed ring buffer rbegin()255 constexpr const_reverse_iterator rbegin() const noexcept // NOLINT(readability-identifier-naming) 256 { 257 return std::make_reverse_iterator(end()); 258 } 259 260 /// @return const iterator to the first element cbegin()261 constexpr const_iterator cbegin() const noexcept // NOLINT(readability-identifier-naming) 262 { 263 return begin(); 264 } 265 266 /// @return a const reverse iterator to the first element of the reversed ring buffer crbegin()267 constexpr const_reverse_iterator crbegin() const noexcept // NOLINT(readability-identifier-naming) 268 { 269 return rbegin(); 270 } 271 272 /// @return iterator to the element following the last element end()273 constexpr iterator end() noexcept // NOLINT(readability-identifier-naming) 274 { 275 auto tmp = tail_; 276 IncrementBufferIndex(tmp); 277 return iterator(&buffer_, tmp); 278 } 279 280 /// @return a reverse iterator to the element following the last element of the reversed ring buffer rend()281 constexpr reverse_iterator rend() noexcept // NOLINT(readability-identifier-naming) 282 { 283 return std::make_reverse_iterator(begin()); 284 } 285 286 /// @return const context iterator to the element following the last element end()287 constexpr const_iterator end() const noexcept // NOLINT(readability-identifier-naming) 288 { 289 auto tmp = tail_; 290 IncrementBufferIndex(tmp); 291 return const_iterator(&buffer_, tmp); 292 } 293 294 /// @return a const context iterator to the element following the last element of the reversed ring buffer rend()295 constexpr const_reverse_iterator rend() const noexcept // NOLINT(readability-identifier-naming) 296 { 297 return std::make_reverse_iterator(begin()); 298 } 299 300 /// @return const iterator to the element following the last element cend()301 constexpr const_iterator cend() const noexcept // NOLINT(readability-identifier-naming) 302 { 303 return end(); 304 } 305 306 /// @return const iterator to the element following the last element of the reversed ring buffer crend()307 constexpr const_reverse_iterator crend() const noexcept // NOLINT(readability-identifier-naming) 308 { 309 return rend(); 310 } 311 312 /// @return reference to first element in ring buffer front()313 constexpr reference front() noexcept // NOLINT(readability-identifier-naming) 314 { 315 return buffer_[head_]; 316 } 317 318 /// @return const reference to first element in ring buffer front()319 constexpr const_reference front() const noexcept // NOLINT(readability-identifier-naming) 320 { 321 return buffer_[head_]; 322 } 323 324 /// @return reference to last element in ring buffer back()325 constexpr reference back() noexcept // NOLINT(readability-identifier-naming) 326 { 327 return buffer_[tail_]; 328 } 329 330 /// @return const reference to last element in ring buffer back()331 constexpr const_reference back() const noexcept // NOLINT(readability-identifier-naming) 332 { 333 return buffer_[tail_]; 334 } 335 336 constexpr reference operator[](size_type index) noexcept 337 { 338 return buffer_[(head_ + index) % buffer_.size()]; 339 } 340 341 constexpr const_reference operator[](size_type index) const noexcept 342 { 343 return buffer_[(head_ + index) % buffer_.size()]; 344 } 345 346 /// @return true if buffer is empty and false otherwise empty()347 [[nodiscard]] constexpr bool empty() const noexcept // NOLINT(readability-identifier-naming) 348 { 349 return size() == 0; 350 } 351 352 /// @return true if buffer is full (all buffer space is used) full()353 [[nodiscard]] constexpr bool full() const noexcept // NOLINT(readability-identifier-naming) 354 { 355 return size() == capacity(); 356 } 357 358 /// @return current ring buffer size size()359 constexpr size_type size() const noexcept // NOLINT(readability-identifier-naming) 360 { 361 return currentSize_; 362 } 363 364 /// @return maximum ring buffer size capacity()365 constexpr size_type capacity() const noexcept // NOLINT(readability-identifier-naming) 366 { 367 return N; 368 } 369 370 /// Erases all elements from the ring buffer. After this call, size() returns zero. clear()371 constexpr void clear() noexcept // NOLINT(readability-identifier-naming) 372 { 373 if constexpr (std::is_class_v<T>) { // NOLINT(readability-braces-around-statements) 374 for (auto &element : *this) { 375 element.~value_type(); 376 } 377 } 378 head_ = 0; 379 tail_ = capacity(); 380 currentSize_ = 0; 381 } 382 383 private: IncrementTail()384 constexpr void IncrementTail() noexcept 385 { 386 IncrementBufferIndex(tail_); 387 if (full()) { 388 IncrementBufferIndex(head_); 389 } else { 390 ++currentSize_; 391 } 392 } 393 DecrementHead()394 constexpr void DecrementHead() noexcept 395 { 396 DecrementBufferIndex(head_); 397 if (full()) { 398 DecrementBufferIndex(tail_); 399 } else { 400 ++currentSize_; 401 } 402 } 403 IncrementBufferIndex(size_type & index)404 constexpr void IncrementBufferIndex(size_type &index) const noexcept 405 { 406 index = (index + 1) % buffer_.size(); 407 } 408 DecrementBufferIndex(size_type & index)409 constexpr void DecrementBufferIndex(size_type &index) const noexcept 410 { 411 index = index == 0U ? N : index - 1; 412 } 413 414 std::array<T, N + 1> buffer_ = {}; 415 size_type head_ = 0U; 416 size_type tail_ = N; 417 size_type currentSize_ = 0U; 418 }; 419 } // namespace ark 420 421 #endif // PANDA_LIBPANDABASE_UTILS_RING_BUFFER_H 422