SpECTRE Documentation Coverage Report
Current view: top level - DataStructures - StaticDeque.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 1.4 % 69 1
Test Date: 2026-10-22 17:29:42
Legend: Lines:     hit not hit

            Line data    Source code
       1            0 : // Distributed under the MIT License.
       2              : // See LICENSE.txt for details.
       3              : 
       4              : #pragma once
       5              : 
       6              : #include <algorithm>
       7              : #include <cstddef>
       8              : #include <iterator>
       9              : #include <limits>
      10              : #include <memory>
      11              : #include <pup.h>
      12              : #include <type_traits>
      13              : 
      14              : #include "Utilities/PrintHelpers.hpp"
      15              : #include "Utilities/Requires.hpp"
      16              : #include "Utilities/StlBoilerplate.hpp"
      17              : 
      18              : namespace static_deque_detail {
      19              : // "The extent to which an implementation determines that a type
      20              : // cannot be an input iterator is unspecified, except that as a
      21              : // minimum integral types shall not qualify as input iterators."
      22              : //   -- N4659 26.2.1.17
      23              : template <typename U>
      24              : constexpr static bool is_input_iterator = not std::is_integral_v<U>;
      25              : }  // namespace static_deque_detail
      26              : 
      27              : /// A class implementing the std::deque interface with static storage.
      28              : ///
      29              : /// Differences from std::deque:
      30              : /// * The size of the container cannot be increased above \p Capacity.
      31              : /// * Operations moving the entire container are \f$O(n)\f$ instead of
      32              : ///   \f$O(1)\f$ in the size and invalidate all references and
      33              : ///   iterators.
      34              : /// * Erasing elements from the front of the queue invalidates all
      35              : ///   iterators (but not references).
      36              : ///
      37              : /// This last point is not a fundamental limitation, but could be
      38              : /// corrected with a more complicated iterator implementation if the
      39              : /// standard behavior is found to be useful.
      40              : template <typename T, size_t Capacity>
      41            1 : class StaticDeque
      42              :     : public stl_boilerplate::RandomAccessSequence<StaticDeque<T, Capacity>,
      43              :                                                    T, true> {
      44              :   // Capacity = size_t max is special and dynamically allocates, but
      45              :   // we don't advertise that here and only intend it to be used
      46              :   // through CircularDeque.
      47              :  public:
      48              :   // Aliases needed in the class definition.  The rest can just be
      49              :   // inherited.
      50            0 :   using size_type = typename StaticDeque::size_type;
      51            0 :   using difference_type = typename StaticDeque::difference_type;
      52            0 :   using iterator = typename StaticDeque::iterator;
      53            0 :   using const_iterator = typename StaticDeque::const_iterator;
      54              : 
      55            0 :   StaticDeque() = default;
      56            0 :   explicit StaticDeque(size_type count);
      57            0 :   StaticDeque(size_type n, const T& value);
      58              :   template <
      59              :       typename InputIterator,
      60              :       Requires<static_deque_detail::is_input_iterator<InputIterator>> = nullptr>
      61            0 :   StaticDeque(InputIterator first, InputIterator last);
      62            0 :   StaticDeque(const StaticDeque& other);
      63            0 :   StaticDeque(StaticDeque&& other);
      64            0 :   StaticDeque(std::initializer_list<T> init);
      65              : 
      66            0 :   ~StaticDeque();
      67              : 
      68            0 :   StaticDeque& operator=(const StaticDeque& other);
      69            0 :   StaticDeque& operator=(StaticDeque&& other);
      70            0 :   StaticDeque& operator=(std::initializer_list<T> init);
      71              : 
      72              :   template <
      73              :       typename InputIterator,
      74              :       Requires<static_deque_detail::is_input_iterator<InputIterator>> = nullptr>
      75            0 :   void assign(InputIterator first, InputIterator last);
      76            0 :   void assign(size_type count, const T& value);
      77            0 :   void assign(std::initializer_list<T> init);
      78              : 
      79            0 :   size_type size() const { return size_; }
      80            0 :   size_type capacity() const { return data_.capacity; }
      81            0 :   static constexpr size_type max_size() { return Capacity; }
      82            0 :   void reserve(size_type count);
      83            0 :   void resize(size_type count);
      84            0 :   void resize(size_type count, const T& value);
      85            0 :   void shrink_to_fit();
      86              : 
      87            0 :   T& operator[](size_type n) { return *entry_address(n); }
      88            0 :   const T& operator[](size_type n) const { return *entry_address(n); }
      89              : 
      90              :   template <typename... Args>
      91            0 :   T& emplace_front(Args&&... args);
      92              :   template <typename... Args>
      93            0 :   T& emplace_back(Args&&... args);
      94              :   template <typename... Args>
      95            0 :   iterator emplace(const_iterator position, Args&&... args);
      96              : 
      97            0 :   void push_front(const T& x) { emplace_front(x); }
      98            0 :   void push_front(T&& x) { emplace_front(std::move(x)); }
      99            0 :   void push_back(const T& x) { emplace_back(x); }
     100            0 :   void push_back(T&& x) { emplace_back(std::move(x)); }
     101              : 
     102            0 :   iterator insert(const_iterator position, const T& x);
     103            0 :   iterator insert(const_iterator position, T&& x);
     104            0 :   iterator insert(const_iterator position, size_type n, const T& x);
     105              :   template <
     106              :       typename InputIterator,
     107              :       Requires<static_deque_detail::is_input_iterator<InputIterator>> = nullptr>
     108            0 :   iterator insert(const_iterator position, InputIterator first,
     109              :                   InputIterator last);
     110            0 :   iterator insert(const_iterator position, std::initializer_list<T> init);
     111              : 
     112            0 :   void pop_front();
     113            0 :   void pop_back();
     114              : 
     115            0 :   iterator erase(const_iterator position);
     116            0 :   iterator erase(const_iterator first, const_iterator last);
     117              : 
     118            0 :   void swap(StaticDeque& other);
     119            0 :   void clear();
     120              : 
     121            0 :   void pup(PUP::er& p);
     122              : 
     123              :  private:
     124            0 :   struct WithCapacityTag {};
     125            0 :   StaticDeque(WithCapacityTag /*meta*/, size_t capacity);
     126              : 
     127              :   // Operations on the middle of the deque are supposed to shift as
     128              :   // few elements as possible.
     129            0 :   bool should_operate_on_back(const const_iterator& first,
     130              :                               const const_iterator& last) const {
     131              :     return (this->end() - last) <= (first - this->begin());
     132              :   }
     133              : 
     134              :   // Index into the internal array of T of logical index n
     135            0 :   size_type internal_index(size_type n) const {
     136              :     auto index = start_position_ + n;
     137              :     if (index >= capacity()) {
     138              :       index -= capacity();
     139              :     }
     140              :     ASSERT(index < capacity(), "Calculated out-of-range index: " << index);
     141              :     return index;
     142              :   }
     143              : 
     144            0 :   const T* entry_address(size_type n) const {
     145              :     return reinterpret_cast<const T*>(data_.get()) + internal_index(n);
     146              :   }
     147              : 
     148            0 :   T* entry_address(size_type n) {
     149              :     return reinterpret_cast<T*>(data_.get()) + internal_index(n);
     150              :   }
     151              : 
     152            0 :   void change_capacity(size_t new_capacity);
     153              : 
     154              :   // This array contains the memory used to store the elements.  It is
     155              :   // treated as an array of T, except that some elements may be
     156              :   // missing, i.e., be segments of memory not containing objects.
     157              :   // Elements are created using placement new and removed by calling
     158              :   // their destructors.
     159              :   //
     160              :   // Logically, the array is considered to be a periodic structure.
     161              :   // The existing elements are stored contiguously in the logical
     162              :   // structure, which may correspond to a contiguous portion of the
     163              :   // actual memory or a segment at the end and a segment at the
     164              :   // beginning.  This segment can grow or shrink at either end,
     165              :   // allowing insertions and removals at the ends without needing to
     166              :   // move any other entries.
     167              :   //
     168              :   // The start_position_ variable stores the index of the start of the
     169              :   // portion of the array that is in use, in the range [0, capacity).
     170              :   // Care must be taken to keep this value in range when the start of
     171              :   // the used memory crosses the end of the storage.  The size_
     172              :   // variable stores the number of existing elements, which must be in
     173              :   // the range [0, capacity].  (This could not be implemented using a
     174              :   // "one past the end" index instead of the size, because that index
     175              :   // is the same for size=0 and size=capacity.)
     176              :   template <size_t Cap, typename = void>
     177            0 :   struct Data {
     178            0 :     static constexpr bool variable_capacity = false;
     179            0 :     alignas(T) std::byte data[sizeof(T) * Cap];
     180            0 :     static constexpr size_t capacity = Cap;
     181              : 
     182            0 :     std::byte* get() { return data; }
     183            0 :     const std::byte* get() const { return data; }
     184              :   };
     185              : 
     186              :   // gcc improperly forbids full specializations
     187              :   // (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85282)
     188              :   template <typename Void>
     189              :   struct Data<std::numeric_limits<size_t>::max(), Void> {
     190              :     static constexpr bool variable_capacity = true;
     191              :     // There doesn't seem to be any alignment guarantee on the
     192              :     // contents of a vector<byte>, so we use operator new directly,
     193              :     // which guarantees alignment suitable for any normal type.
     194              :     std::unique_ptr<std::byte[]> data;
     195              :     size_t capacity = 0;
     196              : 
     197              :     std::byte* get() { return data.get(); }
     198              :     const std::byte* get() const { return data.get(); }
     199              :   };
     200              : 
     201            0 :   Data<Capacity> data_;
     202            0 :   size_type start_position_{0};
     203            0 :   size_type size_{0};
     204              : };
     205              : 
     206              : template <typename T, size_t Capacity>
     207              : StaticDeque<T, Capacity>::StaticDeque(const size_type count) {
     208              :   resize(count);
     209              : }
     210              : 
     211              : template <typename T, size_t Capacity>
     212              : StaticDeque<T, Capacity>::StaticDeque(const size_type n, const T& value) {
     213              :   resize(n, value);
     214              : }
     215              : 
     216              : template <typename T, size_t Capacity>
     217              : template <typename InputIterator,
     218              :           Requires<static_deque_detail::is_input_iterator<InputIterator>>>
     219              : StaticDeque<T, Capacity>::StaticDeque(const InputIterator first,
     220              :                                       const InputIterator last) {
     221              :   assign(first, last);
     222              : }
     223              : 
     224              : template <typename T, size_t Capacity>
     225              : StaticDeque<T, Capacity>::StaticDeque(const StaticDeque& other)
     226              :     : StaticDeque(other.begin(), other.end()) {}
     227              : 
     228              : template <typename T, size_t Capacity>
     229              : StaticDeque<T, Capacity>::StaticDeque(StaticDeque&& other) {
     230              :   *this = std::move(other);
     231              : }
     232              : 
     233              : template <typename T, size_t Capacity>
     234              : StaticDeque<T, Capacity>::StaticDeque(const std::initializer_list<T> init)
     235              :     : StaticDeque(init.begin(), init.end()) {}
     236              : 
     237              : template <typename T, size_t Capacity>
     238              : StaticDeque<T, Capacity>::~StaticDeque() {
     239              :   clear();
     240              : }
     241              : 
     242              : template <typename T, size_t Capacity>
     243              : auto StaticDeque<T, Capacity>::operator=(const StaticDeque& other)
     244              :     -> StaticDeque& {
     245              :   if (size() > other.size()) {
     246              :     resize(other.size());
     247              :   }
     248              :   size_t i = 0;
     249              :   for (; i < size(); ++i) {
     250              :     (*this)[i] = other[i];
     251              :   }
     252              :   for (; i < other.size(); ++i) {
     253              :     push_back(other[i]);
     254              :   }
     255              :   return *this;
     256              : }
     257              : 
     258              : template <typename T, size_t Capacity>
     259              : auto StaticDeque<T, Capacity>::operator=(StaticDeque&& other) -> StaticDeque& {
     260              :   if constexpr (decltype(data_)::variable_capacity) {
     261              :     using std::swap;
     262              :     swap(data_, other.data_);
     263              :     swap(start_position_, other.start_position_);
     264              :     swap(size_, other.size_);
     265              :   } else {
     266              :     if (size() > other.size()) {
     267              :       resize(other.size());
     268              :     }
     269              :     size_t i = 0;
     270              :     for (; i < size(); ++i) {
     271              :       (*this)[i] = std::move(other[i]);
     272              :     }
     273              :     for (; i < other.size(); ++i) {
     274              :       push_back(std::move(other[i]));
     275              :     }
     276              :   }
     277              :   return *this;
     278              : }
     279              : 
     280              : template <typename T, size_t Capacity>
     281              : auto StaticDeque<T, Capacity>::operator=(const std::initializer_list<T> init)
     282              :     -> StaticDeque& {
     283              :   assign(init);
     284              :   return *this;
     285              : }
     286              : 
     287              : template <typename T, size_t Capacity>
     288              : template <typename InputIterator,
     289              :           Requires<static_deque_detail::is_input_iterator<InputIterator>>>
     290              : void StaticDeque<T, Capacity>::assign(InputIterator first,
     291              :                                       const InputIterator last) {
     292              :   // This could be slightly more efficient if this object is not
     293              :   // already empty by assigning to existing objects.
     294              :   clear();
     295              :   if constexpr (std::is_base_of_v<std::forward_iterator_tag,
     296              :                                   typename std::iterator_traits<
     297              :                                       InputIterator>::iterator_category>) {
     298              :     reserve(static_cast<size_type>(std::distance(first, last)));
     299              :   }
     300              :   while (first != last) {
     301              :     push_back(*first++);
     302              :   }
     303              : }
     304              : 
     305              : template <typename T, size_t Capacity>
     306              : void StaticDeque<T, Capacity>::assign(const size_type count, const T& value) {
     307              :   ASSERT(count <= max_size(), count << " exceeds max size " << max_size());
     308              :   // This could be slightly more efficient if this object is not
     309              :   // already empty by assigning to existing objects.
     310              :   clear();
     311              :   resize(count, value);
     312              : }
     313              : 
     314              : template <typename T, size_t Capacity>
     315              : void StaticDeque<T, Capacity>::assign(const std::initializer_list<T> init) {
     316              :   assign(init.begin(), init.end());
     317              : }
     318              : 
     319              : template <typename T, size_t Capacity>
     320              : void StaticDeque<T, Capacity>::reserve(const size_type count) {
     321              :   if constexpr (decltype(data_)::variable_capacity) {
     322              :     if (count > capacity()) {
     323              :       change_capacity(count);
     324              :     }
     325              :   } else {
     326              :     ASSERT(count <= capacity(), "Cannot enlarge a StaticDeque.");
     327              :   }
     328              : }
     329              : 
     330              : template <typename T, size_t Capacity>
     331              : void StaticDeque<T, Capacity>::resize(const size_type count) {
     332              :   ASSERT(count <= max_size(), count << " exceeds max size " << max_size());
     333              :   while (size() > count) {
     334              :     pop_back();
     335              :   }
     336              :   reserve(count);
     337              :   while (size() < count) {
     338              :     emplace_back();
     339              :   }
     340              : }
     341              : 
     342              : template <typename T, size_t Capacity>
     343              : void StaticDeque<T, Capacity>::resize(const size_type count, const T& value) {
     344              :   ASSERT(count <= max_size(), count << " exceeds max size " << max_size());
     345              :   while (size() > count) {
     346              :     pop_back();
     347              :   }
     348              :   reserve(count);
     349              :   while (size() < count) {
     350              :     push_back(value);
     351              :   }
     352              : }
     353              : 
     354              : template <typename T, size_t Capacity>
     355              : void StaticDeque<T, Capacity>::shrink_to_fit() {
     356              :   if constexpr (decltype(data_)::variable_capacity) {
     357              :     change_capacity(size());
     358              :   }
     359              : }
     360              : 
     361              : template <typename T, size_t Capacity>
     362              : template <typename... Args>
     363              : T& StaticDeque<T, Capacity>::emplace_front(Args&&... args) {
     364              :   ASSERT(size() < max_size(), "Container is full.");
     365              :   if constexpr (decltype(data_)::variable_capacity) {
     366              :     if (UNLIKELY(size() == capacity())) {
     367              :       change_capacity(capacity() + 1);
     368              :     }
     369              :   }
     370              :   auto* const new_entry =
     371              :       new (entry_address(capacity() - 1)) T(std::forward<Args>(args)...);
     372              :   // This must be done last in case T's constructor throws an
     373              :   // exception.
     374              :   if (start_position_ == 0) {
     375              :     start_position_ = capacity() - 1;
     376              :   } else {
     377              :     --start_position_;
     378              :   }
     379              :   ++size_;
     380              :   return *new_entry;
     381              : }
     382              : 
     383              : template <typename T, size_t Capacity>
     384              : template <typename... Args>
     385              : T& StaticDeque<T, Capacity>::emplace_back(Args&&... args) {
     386              :   ASSERT(size() < max_size(), "Container is full.");
     387              :   if constexpr (decltype(data_)::variable_capacity) {
     388              :     if (UNLIKELY(size() == capacity())) {
     389              :       change_capacity(capacity() + 1);
     390              :     }
     391              :   }
     392              :   auto* const new_entry =
     393              :       new (entry_address(size())) T(std::forward<Args>(args)...);
     394              :   // This must be done last in case T's constructor throws an
     395              :   // exception.
     396              :   ++size_;
     397              :   return *new_entry;
     398              : }
     399              : 
     400              : template <typename T, size_t Capacity>
     401              : template <typename... Args>
     402            0 : auto StaticDeque<T, Capacity>::emplace(const const_iterator position,
     403              :                                        Args&&... args) -> iterator {
     404              :   ASSERT(size() < max_size(), "Container is full.");
     405              :   if constexpr (decltype(data_)::variable_capacity) {
     406              :     if (UNLIKELY(size() == capacity())) {
     407              :       change_capacity(capacity() + 1);
     408              :     }
     409              :   }
     410              : 
     411              :   if (position == this->begin()) {
     412              :     emplace_front(std::forward<Args>(args)...);
     413              :     return this->begin();
     414              :   } else if (position == this->end()) {
     415              :     emplace_back(std::forward<Args>(args)...);
     416              :     return this->end() - 1;
     417              :   }
     418              : 
     419              :   const auto element_to_assign = position - this->begin();
     420              :   if (should_operate_on_back(position, position)) {
     421              :     emplace_back(std::move(this->back()));
     422              :     std::move_backward(this->begin() + element_to_assign, this->end() - 2,
     423              :                        this->end() - 1);
     424              :   } else {
     425              :     emplace_front(std::move(this->front()));
     426              :     std::move(this->begin() + 2, this->begin() + element_to_assign + 1,
     427              :               this->begin() + 1);
     428              :   }
     429              : 
     430              :   const auto new_element = this->begin() + element_to_assign;
     431              :   // emplace in the middle does not construct in place.  STL
     432              :   // containers also have this behavior.
     433              :   *new_element = T(std::forward<Args>(args)...);
     434              :   return new_element;
     435              : }
     436              : 
     437              : template <typename T, size_t Capacity>
     438              : auto StaticDeque<T, Capacity>::insert(const const_iterator position, const T& x)
     439              :     -> iterator {
     440              :   return emplace(position, x);
     441              : }
     442              : 
     443              : template <typename T, size_t Capacity>
     444              : auto StaticDeque<T, Capacity>::insert(const const_iterator position, T&& x)
     445              :     -> iterator {
     446              :   return emplace(position, std::move(x));
     447              : }
     448              : 
     449              : template <typename T, size_t Capacity>
     450              : auto StaticDeque<T, Capacity>::insert(const const_iterator position,
     451              :                                       const size_type n, const T& x)
     452              :     -> iterator {
     453              :   reserve(size() + n);
     454              :   // In theory we could insert these in place and save a bunch of
     455              :   // moves, but keeping track of when elements must be assigned
     456              :   // vs. constructed would be a pain, and this can't be that common an
     457              :   // operation.
     458              :   if (should_operate_on_back(position, position)) {
     459              :     // We are about to invalidate position.
     460              :     const auto insert_index = position - this->begin();
     461              :     const auto old_size = static_cast<difference_type>(size());
     462              :     for (size_type i = 0; i < n; ++i) {
     463              :       emplace_back(x);
     464              :     }
     465              :     const auto new_position = this->begin() + insert_index;
     466              :     const auto old_end = this->begin() + old_size;
     467              :     std::rotate(new_position, old_end, this->end());
     468              :     return new_position;
     469              :   } else {
     470              :     // We are about to invalidate position.
     471              :     const auto insert_index = position - this->begin();
     472              :     const auto insert_back_index = this->end() - position;
     473              :     const auto old_size = static_cast<difference_type>(size());
     474              :     for (size_type i = 0; i < n; ++i) {
     475              :       emplace_front(x);
     476              :     }
     477              :     const auto new_position = this->begin() + insert_index;
     478              :     const auto new_position_end = this->end() - insert_back_index;
     479              :     const auto old_begin = this->end() - old_size;
     480              :     std::rotate(this->begin(), old_begin, new_position_end);
     481              :     return new_position;
     482              :   }
     483              : }
     484              : 
     485              : template <typename T, size_t Capacity>
     486              : template <typename InputIterator,
     487              :           Requires<static_deque_detail::is_input_iterator<InputIterator>>>
     488            0 : auto StaticDeque<T, Capacity>::insert(const const_iterator position,
     489              :                                       InputIterator first,
     490              :                                       const InputIterator last) -> iterator {
     491              :   if constexpr (std::is_base_of_v<std::forward_iterator_tag,
     492              :                                   typename std::iterator_traits<
     493              :                                       InputIterator>::iterator_category>) {
     494              :     reserve(size() + static_cast<size_type>(std::distance(first, last)));
     495              :   }
     496              :   // The iterators could be single-pass, so we can't clear space and
     497              :   // then insert the new elements.  We also can't do repeated single
     498              :   // insertions because that's too inefficient.  Instead, we have to
     499              :   // create the new elements at the end and then rotate them into
     500              :   // place.
     501              :   if (should_operate_on_back(position, position)) {
     502              :     // We are about to invalidate position.
     503              :     const auto insert_index = position - this->begin();
     504              :     const auto old_size = static_cast<difference_type>(size());
     505              :     while (first != last) {
     506              :       emplace_back(*first++);
     507              :     }
     508              :     const auto new_position = this->begin() + insert_index;
     509              :     const auto old_end = this->begin() + old_size;
     510              :     std::rotate(new_position, old_end, this->end());
     511              :     return new_position;
     512              :   } else {
     513              :     // We are about to invalidate position.
     514              :     const auto insert_index = position - this->begin();
     515              :     const auto insert_back_index = this->end() - position;
     516              :     const auto old_size = static_cast<difference_type>(size());
     517              :     while (first != last) {
     518              :       emplace_front(*first++);
     519              :     }
     520              :     const auto new_position = this->begin() + insert_index;
     521              :     const auto new_position_end = this->end() - insert_back_index;
     522              :     const auto old_begin = this->end() - old_size;
     523              :     std::reverse(this->begin(), old_begin);
     524              :     std::rotate(this->begin(), old_begin, new_position_end);
     525              :     return new_position;
     526              :   }
     527              : }
     528              : 
     529              : template <typename T, size_t Capacity>
     530              : auto StaticDeque<T, Capacity>::insert(const const_iterator position,
     531              :                                       const std::initializer_list<T> init)
     532              :     -> iterator {
     533              :   return insert(position, init.begin(), init.end());
     534              : }
     535              : 
     536              : template <typename T, size_t Capacity>
     537              : void StaticDeque<T, Capacity>::pop_front() {
     538              :   ASSERT(not this->empty(), "Container is empty.");
     539              :   if (start_position_ == capacity() - 1) {
     540              :     start_position_ = 0;
     541              :   } else {
     542              :     ++start_position_;
     543              :   }
     544              :   --size_;
     545              :   // Calling the destructor is sufficient to remove the element.
     546              :   // There is no special "placement delete".
     547              :   entry_address(capacity() - 1)->~T();
     548              : }
     549              : 
     550              : template <typename T, size_t Capacity>
     551              : void StaticDeque<T, Capacity>::pop_back() {
     552              :   ASSERT(not this->empty(), "Container is empty.");
     553              :   --size_;
     554              :   // Calling the destructor is sufficient to remove the element.
     555              :   // There is no special "placement delete".
     556              :   entry_address(size())->~T();
     557              : }
     558              : 
     559              : template <typename T, size_t Capacity>
     560              : auto StaticDeque<T, Capacity>::erase(const const_iterator position)
     561              :     -> iterator {
     562              :   return erase(position, position + 1);
     563              : }
     564              : 
     565              : template <typename T, size_t Capacity>
     566              : auto StaticDeque<T, Capacity>::erase(const const_iterator first,
     567              :                                      const const_iterator last) -> iterator {
     568              :   const auto result_offset = first - this->begin();
     569              :   auto num_to_remove = last - first;
     570              :   if (num_to_remove == 0) {
     571              :     return last - this->begin() + this->begin();
     572              :   }
     573              :   if (should_operate_on_back(first, last)) {
     574              :     std::move(last, this->cend(), first - this->begin() + this->begin());
     575              :     while (num_to_remove-- != 0) {
     576              :       pop_back();
     577              :     }
     578              :   } else {
     579              :     std::move_backward(this->cbegin(), first,
     580              :                        last - this->begin() + this->begin());
     581              :     while (num_to_remove-- != 0) {
     582              :       pop_front();
     583              :     }
     584              :   }
     585              :   return this->begin() + result_offset;
     586              : }
     587              : 
     588              : template <typename T, size_t Capacity>
     589              : void StaticDeque<T, Capacity>::swap(StaticDeque& other) {
     590              :   using std::swap;
     591              :   swap(*this, other);
     592              : }
     593              : 
     594              : template <typename T, size_t Capacity>
     595              : void StaticDeque<T, Capacity>::clear() {
     596              :   while (not this->empty()) {
     597              :     pop_back();
     598              :   }
     599              : }
     600              : 
     601              : template <typename T, size_t Capacity>
     602              : void StaticDeque<T, Capacity>::pup(PUP::er& p) {
     603              :   // If performance when serializing simple types becomes an issue, we
     604              :   // can just send the array of bytes and the other member variables,
     605              :   // which will probably perform better (unless, perhaps, the
     606              :   // container is nearly empty).  That can't be done for types that
     607              :   // are not trivially copyable, which would still need to use
     608              :   // something like the current implementation.
     609              :   size_type sz = size();
     610              :   p | sz;
     611              :   resize(sz);
     612              :   for (auto& entry : *this) {
     613              :     p | entry;
     614              :   }
     615              : }
     616              : 
     617              : template <typename T, size_t Capacity>
     618            0 : StaticDeque<T, Capacity>::StaticDeque(
     619              :     StaticDeque<T, Capacity>::WithCapacityTag /*meta*/, const size_t capacity)
     620              :     : data_{std::unique_ptr<std::byte[]>(new std::byte[capacity * sizeof(T)]),
     621              :             capacity} {}
     622              : 
     623              : template <typename T, size_t Capacity>
     624              : void StaticDeque<T, Capacity>::change_capacity(const size_t new_capacity) {
     625              :   ASSERT(decltype(data_)::variable_capacity, "Trying to resize an array.");
     626              :   ASSERT(new_capacity >= size(), "Insufficient space for existing elements.");
     627              :   if constexpr (decltype(data_)::variable_capacity) {
     628              :     if (new_capacity == capacity()) {
     629              :       return;
     630              :     }
     631              :     if (new_capacity == 0) {
     632              :       data_.data.reset();
     633              :       data_.capacity = 0;
     634              :       return;
     635              :     }
     636              : 
     637              :     StaticDeque new_deque(WithCapacityTag{}, new_capacity);
     638              :     new_deque.assign(std::make_move_iterator(this->begin()),
     639              :                      std::make_move_iterator(this->end()));
     640              :     *this = std::move(new_deque);
     641              :   }
     642              : }
     643              : 
     644              : template <typename T, size_t Capacity>
     645            0 : void swap(StaticDeque<T, Capacity>& x, StaticDeque<T, Capacity>& y) {
     646              :   // swap on CircularBuffers will prefer the generic one in std over
     647              :   // this one, but this still gets called by some member functions.
     648              :   // The generic implementation will correctly swap the allocations
     649              :   // instead of working with individual elements.
     650              :   if constexpr (Capacity == std::numeric_limits<size_t>::max()) {
     651              :     std::swap(x, y);
     652              :     return;
     653              :   }
     654              : 
     655              :   const auto initial_size = x.size();
     656              :   if (initial_size > y.size()) {
     657              :     return swap(y, x);
     658              :   }
     659              :   for (size_t i = 0; i < initial_size; ++i) {
     660              :     using std::swap;
     661              :     swap(x[i], y[i]);
     662              :   }
     663              :   const auto to_move =
     664              :       y.begin() +
     665              :       static_cast<typename StaticDeque<T, Capacity>::difference_type>(
     666              :           initial_size);
     667              :   x.insert(x.end(), std::move_iterator(to_move), std::move_iterator(y.end()));
     668              :   y.erase(to_move, y.end());
     669              : }
     670              : 
     671              : template <typename T, size_t Capacity>
     672            0 : std::ostream& operator<<(std::ostream& s,
     673              :                          const StaticDeque<T, Capacity>& deque) {
     674              :   sequence_print_helper(s, deque.begin(), deque.end());
     675              :   return s;
     676              : }
        

Generated by: LCOV version 2.0-1