circular_array.hpp
Go to the documentation of this file.
1 // Copyright 2024 Ekumen, Inc.
2 //
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 #ifndef BELUGA_CONTAINERS_CIRCULAR_ARRAY_HPP
16 #define BELUGA_CONTAINERS_CIRCULAR_ARRAY_HPP
17 
18 #include <array>
19 #include <cstdint>
20 #include <iterator>
21 #include <stdexcept>
22 #include <type_traits>
23 #include <utility>
24 
26 
32 namespace beluga {
33 
35 enum class CircularArrayFeatureFlags : std::int8_t {
36  kNone = 0x00,
37  kRolloverOnWrite = 0x01,
38  kExtrapolateOnRead = 0x02,
40  kLayoutReversal = 0x04
42 };
44 
47  return static_cast<CircularArrayFeatureFlags>( // NOLINT(lang-analyzer-optin.core.EnumCastOutOfRange)
48  static_cast<std::underlying_type_t<CircularArrayFeatureFlags>>(lflag) |
49  static_cast<std::underlying_type_t<CircularArrayFeatureFlags>>(rflag));
50 }
51 
54  return static_cast<bool>(
55  static_cast<std::underlying_type_t<CircularArrayFeatureFlags>>(mask) &
56  static_cast<std::underlying_type_t<CircularArrayFeatureFlags>>(flag));
57 }
58 
60 
75 template <typename T, std::size_t N, CircularArrayFeatureFlags F = CircularArrayFeatureFlags::kNone>
77  public:
79  using value_type = T;
81  using size_type = std::size_t;
83  using difference_type = std::ptrdiff_t;
87  using const_reference = const value_type&;
89  using pointer = value_type*;
91  using const_pointer = const value_type*;
92 
94  using allocator_type = void;
95 
101  using reverse_iterator = std::reverse_iterator<iterator>;
103  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
104 
106 
109  CircularArray() = default;
110 
112 
122  template <
123  typename Iterator,
124  typename Sentinel,
125  typename = std::enable_if_t<std::is_same_v<T, typename std::iterator_traits<Iterator>::value_type>>>
126  CircularArray(Iterator first, Sentinel last) { // NOLINT(readability-non-const-parameter)
127  if constexpr (F & CircularArrayFeatureFlags::kLayoutReversal) {
128  tail_index_ = N - 1;
129  for (size_ = 0; size_ < N && first != last; ++size_, ++first) {
130  data_[tail_index_ - size_] = *first;
131  }
132  if constexpr (!(F & CircularArrayFeatureFlags::kRolloverOnWrite)) {
133  if (first != last) {
134  throw std::length_error{"Range does not fit in circular array"};
135  }
136  }
137  } else {
138  const auto n = static_cast<size_type>(std::distance(first, last));
139  if (n > N) {
140  if constexpr (!(F & CircularArrayFeatureFlags::kRolloverOnWrite)) {
141  throw std::length_error{"Range does not fit in circular array"};
142  } else {
143  std::advance(first, n - N);
144  }
145  }
146  for (size_ = 0; size_ < N && first != last; ++size_, ++first) {
147  data_[size_] = *first;
148  }
149  tail_index_ = size_ - 1;
150  }
151  }
152 
154 
162  template <std::size_t M, typename = std::enable_if_t<M >= 1 && M <= N>>
163  explicit CircularArray(T (&&data)[M]) : tail_index_(M - 1), size_(M) { // NOLINT(modernize-avoid-c-arrays)
164  if constexpr (F & CircularArrayFeatureFlags::kLayoutReversal) {
165  for (std::size_t i = 0; i < M; ++i)
166  data_[i] = data[M - i - 1];
167  } else {
168  for (std::size_t i = 0; i < M; ++i)
169  data_[i] = data[i];
170  }
171  }
172 
174  [[nodiscard]] constexpr iterator begin() noexcept { return iterator(this); }
176  [[nodiscard]] constexpr const_iterator begin() const noexcept { return const_iterator(this); }
178  [[nodiscard]] constexpr const_iterator cbegin() const noexcept { return const_iterator(this); }
179 
181  [[nodiscard]] constexpr iterator end() noexcept { return iterator(this, size()); }
183  [[nodiscard]] constexpr const_iterator end() const noexcept { return const_iterator(this, effective_size()); }
185  [[nodiscard]] constexpr const_iterator cend() const noexcept { return const_iterator(this, effective_size()); }
186 
188  [[nodiscard]] constexpr reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
190  [[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); }
192  [[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(cend()); }
193 
195  [[nodiscard]] constexpr reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
197  [[nodiscard]] constexpr const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); }
199  [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator(cbegin()); }
200 
202 
208  template <bool Enabled = !(F & CircularArrayFeatureFlags::kLayoutReversal)>
209  std::enable_if_t<Enabled> push_back(value_type value) {
210  static_assert(
212  "Cannot push_back() when the layout reversal feature is enabled.");
213  if constexpr (!(F & CircularArrayFeatureFlags::kRolloverOnWrite)) {
214  if (size_ == N) {
215  throw std::length_error{"Circular array reached its maximum size"};
216  }
217  }
218  if (++tail_index_ == N)
219  tail_index_ = 0;
220  data_[tail_index_] = std::move(value);
221  size_ = std::min(size_ + 1, N);
222  }
223 
225 
231  template <bool Enabled = (F & CircularArrayFeatureFlags::kLayoutReversal)>
232  std::enable_if_t<Enabled> push_front(value_type value) {
233  static_assert(
235  "Cannot push_front() when the layout reversal feature is not enabled.");
236  if constexpr (!(F & CircularArrayFeatureFlags::kRolloverOnWrite)) {
237  if (size_ == N) {
238  throw std::length_error{"Circular array reached its maximum size"};
239  }
240  }
241  if (++tail_index_ == N)
242  tail_index_ = 0;
243  data_[tail_index_] = std::move(value);
244  size_ = std::min(size_ + 1, N);
245  }
246 
248 
253  template <bool Enabled = (F & CircularArrayFeatureFlags::kLayoutReversal)>
254  std::enable_if_t<Enabled> pop_back() noexcept {
255  --size_;
256  }
257 
259 
264  template <bool Enabled = !(F & CircularArrayFeatureFlags::kLayoutReversal)>
265  std::enable_if_t<Enabled> pop_front() noexcept {
266  --size_;
267  }
268 
270 
273  [[nodiscard]] constexpr reference back() noexcept {
274  if constexpr (F & CircularArrayFeatureFlags::kLayoutReversal) {
275  return data_[head_index()];
276  } else {
277  return data_[tail_index_];
278  }
279  }
281 
284  [[nodiscard]] constexpr const_reference back() const noexcept {
285  if constexpr (F & CircularArrayFeatureFlags::kLayoutReversal) {
286  return data_[head_index()];
287  } else {
288  return data_[tail_index_];
289  }
290  }
291 
293 
296  [[nodiscard]] constexpr reference front() noexcept {
297  if constexpr (F & CircularArrayFeatureFlags::kLayoutReversal) {
298  return data_[tail_index_];
299  } else {
300  return data_[head_index()];
301  }
302  }
303 
305 
308  [[nodiscard]] constexpr const_reference front() const noexcept {
309  if constexpr (F & CircularArrayFeatureFlags::kLayoutReversal) {
310  return data_[tail_index_];
311  } else {
312  return data_[head_index()];
313  }
314  }
315 
317 
320  [[nodiscard]] constexpr reference at(size_type index) {
321  if (index >= effective_size()) {
322  throw std::out_of_range{"Index out of circular array range"};
323  }
324  return (*this)[index];
325  }
326 
328 
331  [[nodiscard]] constexpr const_reference at(size_type index) const {
332  if (index >= effective_size()) {
333  throw std::out_of_range{"Index out of circular array range"};
334  }
335  return (*this)[index];
336  }
337 
339 
342  [[nodiscard]] constexpr reference operator[](size_type index) noexcept {
343  if constexpr (F & CircularArrayFeatureFlags::kLayoutReversal) {
344  return data_[static_cast<size_type>(index > tail_index_) * N + tail_index_ - index];
345  } else {
346  return data_[static_cast<size_type>(size_ - index > tail_index_ + 1) * N + tail_index_ - (size_ - index - 1)];
347  }
348  }
349 
351 
354  [[nodiscard]] constexpr const_reference operator[](size_type index) const noexcept {
356  index = std::min(index, size_ - 1);
357  }
358  if constexpr (F & CircularArrayFeatureFlags::kLayoutReversal) {
359  return data_[static_cast<size_type>(index > tail_index_) * N + tail_index_ - index];
360  } else {
361  return data_[static_cast<size_type>(size_ - index > tail_index_ + 1) * N + tail_index_ - (size_ - index - 1)];
362  }
363  }
364 
366 
372  void fill(const T& value) {
373  while (size_ < N) {
374  tail_index_ = (tail_index_ + 1) % N;
375  data_[tail_index_] = value;
376  ++size_;
377  }
378  }
379 
381 
384  void clear() noexcept { size_ = 0; }
385 
387 
392  template <CircularArrayFeatureFlags G>
393  void swap(CircularArray<T, N, G>& other) noexcept(std::is_nothrow_swappable_v<T>) {
394  using std::swap;
395  swap(data_, other.data_);
396  swap(size_, other.size_);
397  swap(tail_index_, other.tail_index_);
398  }
399 
400  template <typename U, std::size_t M, CircularArrayFeatureFlags G>
401  friend class CircularArray;
402 
404 
408  [[nodiscard]] constexpr T* data() noexcept { return data_.data(); }
409 
411 
415  [[nodiscard]] constexpr const T* data() const noexcept { return data_.data(); }
416 
418  [[nodiscard]] constexpr bool full() const noexcept { return size_ == N; }
419 
421  [[nodiscard]] constexpr bool empty() const noexcept { return size_ == 0U; }
422 
424  [[nodiscard]] constexpr size_type size() const noexcept { return size_; }
425 
427  [[nodiscard]] constexpr size_type max_size() const noexcept { return N; }
428 
430 
436  [[nodiscard]] constexpr size_type effective_size() const noexcept {
438  return static_cast<size_type>(size_ > 0) * N;
439  } else {
440  return size_;
441  }
442  }
443 
444  private:
445  // Computes the head index of the array.
446  [[nodiscard]] constexpr size_type head_index() const noexcept {
447  return static_cast<size_type>(size_ > tail_index_ + 1) * N + tail_index_ - size_ + 1;
448  }
449 
450  std::array<T, N> data_;
453 };
454 
456 
460 template <typename T, std::size_t N>
461 using RollingWindow = CircularArray<
462  T,
463  N,
466 
468 
472 template <typename T, std::size_t N, CircularArrayFeatureFlags F>
474  if constexpr (F & CircularArrayFeatureFlags::kLayoutReversal) {
475  array.push_front(std::move(value));
476  } else {
477  array.push_back(std::move(value));
478  }
479  return array;
480 }
481 
483 
486 template <std::size_t I, class T, std::size_t N, CircularArrayFeatureFlags F>
487 constexpr T& get(CircularArray<T, N, F>& array) noexcept {
488  return array[I];
489 }
490 
492 
495 template <std::size_t I, class T, std::size_t N, CircularArrayFeatureFlags F>
496 constexpr T&& get(CircularArray<T, N, F>&& array) noexcept {
497  return array[I];
498 }
499 
501 
504 template <std::size_t I, class T, std::size_t N, CircularArrayFeatureFlags F>
505 constexpr const T& get(const CircularArray<T, N, F>& array) noexcept {
506  return array[I];
507 }
508 
510 
513 template <std::size_t I, class T, std::size_t N, CircularArrayFeatureFlags F>
514 constexpr const T&& get(const CircularArray<T, N, F>&& array) noexcept {
515  return array[I];
516 }
517 
519 
522 template <typename T, std::size_t N, CircularArrayFeatureFlags F, CircularArrayFeatureFlags G>
524  a.swap(b);
525 }
526 
527 } // namespace beluga
528 
529 namespace std {
530 
532 template <typename T, std::size_t N, beluga::CircularArrayFeatureFlags F>
533 struct tuple_size<beluga::CircularArray<T, N, F>> : std::integral_constant<std::size_t, N> {};
534 
536 template <std::size_t I, typename T, std::size_t N, beluga::CircularArrayFeatureFlags F>
537 struct tuple_element<I, beluga::CircularArray<T, N, F>> {
538  using type = T;
539 };
540 
541 } // namespace std
542 
543 #endif
beluga::CircularArray::begin
constexpr const_iterator begin() const noexcept
Returns a constant iterator pointing to the front of the array.
Definition: circular_array.hpp:176
beluga::CircularArray::CircularArray
friend class CircularArray
Definition: circular_array.hpp:401
beluga::CircularArray::data_
std::array< T, N > data_
Definition: circular_array.hpp:450
beluga::get
constexpr T & get(CircularArray< T, N, F > &array) noexcept
Gets an lvalue reference to the ith value in a given array.
Definition: circular_array.hpp:487
beluga::CircularArray::back
constexpr const_reference back() const noexcept
Returns a constant reference to the value at the back of the array.
Definition: circular_array.hpp:284
beluga::CircularArray::end
constexpr const_iterator end() const noexcept
Returns a constant iterator pointing past the back of the array.
Definition: circular_array.hpp:183
beluga::CircularArray::empty
constexpr bool empty() const noexcept
Returns true if the array is empty, false otherwise.
Definition: circular_array.hpp:421
beluga::CircularArray::CircularArray
CircularArray(Iterator first, Sentinel last)
Constructs array from a pair of iterators.
Definition: circular_array.hpp:126
beluga::swap
constexpr void swap(CircularArray< T, N, F > &a, CircularArray< T, N, G > &b)
Swaps arrays a and b.
Definition: circular_array.hpp:523
beluga::CircularArray::full
constexpr bool full() const noexcept
Returns true if the array is full, false otherwise.
Definition: circular_array.hpp:418
beluga::CircularArray< state_type, 2 >::allocator_type
void allocator_type
Allocator type of the array (required in range-v3 10.0).
Definition: circular_array.hpp:94
std::tuple_element< I, beluga::CircularArray< T, N, F > >::type
T type
! Always T since circular arrays are homogeneous.
Definition: circular_array.hpp:538
beluga::operator|
constexpr CircularArrayFeatureFlags operator|(CircularArrayFeatureFlags lflag, CircularArrayFeatureFlags rflag)
Bitwise OR operator overload to combine two feature flags in a single mask-like flag.
Definition: circular_array.hpp:46
beluga::CircularArray::pop_front
std::enable_if_t< Enabled > pop_front() noexcept
Pops a value from the front of the array.
Definition: circular_array.hpp:265
beluga::CircularArray::end
constexpr iterator end() noexcept
Returns an iterator pointing past the back of the array.
Definition: circular_array.hpp:181
beluga::CircularArray::push_front
std::enable_if_t< Enabled > push_front(value_type value)
Pushes a value at the front of the array.
Definition: circular_array.hpp:232
beluga::CircularArrayFeatureFlags::kRolloverOnWrite
@ kRolloverOnWrite
beluga::CircularArray< state_type, 2 >::const_reference
const value_type & const_reference
Constant value reference type of the array.
Definition: circular_array.hpp:87
beluga::CircularArray::swap
void swap(CircularArray< T, N, G > &other) noexcept(std::is_nothrow_swappable_v< T >)
Swaps array with another.
Definition: circular_array.hpp:393
beluga::CircularArray::crend
constexpr const_reverse_iterator crend() const noexcept
Returns a constant reverse iterator pointing past the front of the array.
Definition: circular_array.hpp:199
beluga::CircularArray::size
constexpr size_type size() const noexcept
Returns the current array size.
Definition: circular_array.hpp:424
beluga::CircularArray::cend
constexpr const_iterator cend() const noexcept
Returns a constant iterator pointing past the back of the array.
Definition: circular_array.hpp:185
beluga::CircularArray::operator[]
constexpr reference operator[](size_type index) noexcept
Returns a reference to the array value at the given index.
Definition: circular_array.hpp:342
beluga::CircularArray::front
constexpr reference front() noexcept
Returns a reference to the value at the front of the array.
Definition: circular_array.hpp:296
beluga::CircularArray::crbegin
constexpr const_reverse_iterator crbegin() const noexcept
Returns a constant reverse iterator pointing to the back of the array.
Definition: circular_array.hpp:192
beluga::CircularArray< state_type, 2 >::reference
value_type & reference
Value reference type of the array.
Definition: circular_array.hpp:85
beluga::operator<<
CircularArray< T, N, F > & operator<<(CircularArray< T, N, F > &array, T value)
Convenient stream operator overload to push a value to a circular array.
Definition: circular_array.hpp:473
beluga::CircularArray::at
constexpr reference at(size_type index)
Returns a reference to the array value at the given index.
Definition: circular_array.hpp:320
beluga::CircularArray::operator[]
constexpr const_reference operator[](size_type index) const noexcept
Returns a constant reference to the array value at the given index.
Definition: circular_array.hpp:354
beluga::IndexingIterator
A random access iterator for any indexable container.
Definition: indexing_iterator.hpp:38
beluga::RollingWindow
CircularArray< T, N, CircularArrayFeatureFlags::kRolloverOnWrite|CircularArrayFeatureFlags::kExtrapolateOnRead|CircularArrayFeatureFlags::kLayoutReversal > RollingWindow
Convenient type alias for a circular array that behaves like a rolling window.
Definition: circular_array.hpp:465
beluga::CircularArray::data
constexpr const T * data() const noexcept
Returns a constant pointer to the underlying array data.
Definition: circular_array.hpp:415
beluga::CircularArray::max_size
constexpr size_type max_size() const noexcept
Returns the maximum array size.
Definition: circular_array.hpp:427
beluga::CircularArrayFeatureFlags
CircularArrayFeatureFlags
Feature flags for circular arrays.
Definition: circular_array.hpp:35
beluga::CircularArray
An implementation of generic, non-threadsafe circular array.
Definition: circular_array.hpp:76
beluga::CircularArray< state_type, 2 >::reverse_iterator
std::reverse_iterator< iterator > reverse_iterator
Reverse iterator type of the array.
Definition: circular_array.hpp:101
beluga::CircularArray::pop_back
std::enable_if_t< Enabled > pop_back() noexcept
Pops a value from the back of the array.
Definition: circular_array.hpp:254
beluga::CircularArray< state_type, 2 >::size_type
std::size_t size_type
Size type of the array.
Definition: circular_array.hpp:81
indexing_iterator.hpp
Implementation of a random access iterator for indexable containers.
beluga::CircularArray::back
constexpr reference back() noexcept
Returns a reference to the value at the back of the array.
Definition: circular_array.hpp:273
beluga::CircularArray::cbegin
constexpr const_iterator cbegin() const noexcept
Returns a constant iterator pointing to the front of the array.
Definition: circular_array.hpp:178
beluga::CircularArray::data
constexpr T * data() noexcept
Returns a pointer to the underlying array data.
Definition: circular_array.hpp:408
beluga::CircularArrayFeatureFlags::kLayoutReversal
@ kLayoutReversal
beluga::CircularArray< state_type, 2 >::difference_type
std::ptrdiff_t difference_type
Size difference type of the array.
Definition: circular_array.hpp:83
beluga::CircularArray< state_type, 2 >::value_type
state_type value_type
Value type of the array.
Definition: circular_array.hpp:79
beluga::CircularArrayFeatureFlags::kExtrapolateOnRead
@ kExtrapolateOnRead
beluga::CircularArray::effective_size
constexpr size_type effective_size() const noexcept
Returns the effective array size.
Definition: circular_array.hpp:436
std
Definition: circular_array.hpp:529
beluga::operator&
constexpr bool operator&(CircularArrayFeatureFlags mask, CircularArrayFeatureFlags flag)
Bitwise AND operator overload to check of the presence of a feature flag in a feature mask.
Definition: circular_array.hpp:53
beluga::CircularArray< state_type, 2 >::const_pointer
const value_type * const_pointer
Constant value pointer type of the arra.y.
Definition: circular_array.hpp:91
beluga::CircularArray::at
constexpr const_reference at(size_type index) const
Returns a constant reference to the array value at the given index.
Definition: circular_array.hpp:331
beluga::CircularArray::fill
void fill(const T &value)
Fills array to its maximum size with a given value.
Definition: circular_array.hpp:372
beluga::CircularArray::iterator
IndexingIterator< CircularArray< T, N, F > > iterator
Iterator type of the array.
Definition: circular_array.hpp:97
beluga::CircularArray::CircularArray
CircularArray(T(&&data)[M])
Constructs array from an aggregate.
Definition: circular_array.hpp:163
beluga::CircularArray::clear
void clear() noexcept
Clears array.
Definition: circular_array.hpp:384
beluga::CircularArray::tail_index_
size_type tail_index_
Definition: circular_array.hpp:451
beluga::CircularArray< state_type, 2 >::const_reverse_iterator
std::reverse_iterator< const_iterator > const_reverse_iterator
Constant reverse iterator type of the array.
Definition: circular_array.hpp:103
beluga::CircularArray::const_iterator
IndexingIterator< const CircularArray< T, N, F > > const_iterator
Constant iterator type of the array.
Definition: circular_array.hpp:99
beluga::CircularArray::begin
constexpr iterator begin() noexcept
Returns an iterator pointing to the front of the array.
Definition: circular_array.hpp:174
beluga::CircularArray::rend
constexpr const_reverse_iterator rend() const noexcept
Returns a constant reverse iterator pointing past the front of the array.
Definition: circular_array.hpp:197
beluga::CircularArray::rend
constexpr reverse_iterator rend() noexcept
Returns a reverse iterator pointing past the front of the array.
Definition: circular_array.hpp:195
beluga::CircularArray::rbegin
constexpr reverse_iterator rbegin() noexcept
Returns a reverse iterator pointing to the back of the array.
Definition: circular_array.hpp:188
beluga::CircularArray::head_index
constexpr size_type head_index() const noexcept
Definition: circular_array.hpp:446
beluga::CircularArray::push_back
std::enable_if_t< Enabled > push_back(value_type value)
Pushes a value to the back of the array.
Definition: circular_array.hpp:209
beluga::CircularArray< state_type, 2 >::pointer
value_type * pointer
Value pointer type of the array.
Definition: circular_array.hpp:89
beluga::CircularArray::front
constexpr const_reference front() const noexcept
Returns a constant reference to the value at the front of the array.
Definition: circular_array.hpp:308
beluga::CircularArrayFeatureFlags::kNone
@ kNone
beluga::CircularArray::size_
size_type size_
Definition: circular_array.hpp:452
beluga
The main Beluga namespace.
Definition: 3d_embedding.hpp:21
beluga::CircularArray::rbegin
constexpr const_reverse_iterator rbegin() const noexcept
Returns a constant reverse iterator pointing to the back of the array.
Definition: circular_array.hpp:190


beluga
Author(s):
autogenerated on Tue Jul 16 2024 02:59:53