• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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