text: ﻿// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
  // Distributed under the MIT License (http://opensource.org/licenses/MIT)
  
  // circular q view of std::vector.
  #pragma once
  
  #include <vector>
  #include <cassert>
  
  namespace spdlog {
  namespace details {
  template<typename T>
  class circular_q
  {
      size_t max_items_ = 0;
      typename std::vector<T>::size_type head_ = 0;
      typename std::vector<T>::size_type tail_ = 0;
      size_t overrun_counter_ = 0;
      std::vector<T> v_;
  
  public:
      using value_type = T;
  
      // empty ctor - create a disabled queue with no elements allocated at all
      circular_q() = default;
  
      explicit circular_q(size_t max_items)
          : max_items_(max_items + 1) // one item is reserved as marker for full q
          , v_(max_items_)
      {}
  
      circular_q(const circular_q &) = default;
      circular_q &operator=(const circular_q &) = default;
  
      // move cannot be default,
      // since we need to reset head_, tail_, etc to zero in the moved object
      circular_q(circular_q &&other) SPDLOG_NOEXCEPT
      {
          copy_moveable(std::move(other));
      }
  
      circular_q &operator=(circular_q &&other) SPDLOG_NOEXCEPT
      {
          copy_moveable(std::move(other));
          return *this;
      }
  
      // push back, overrun (oldest) item if no room left
      void push_back(T &&item)
      {
          if (max_items_ > 0)
          {
              v_[tail_] = std::move(item);
              tail_ = (tail_ + 1) % max_items_;
  
              if (tail_ == head_) // overrun last item if full
              {
                  head_ = (head_ + 1) % max_items_;
                  ++overrun_counter_;
              }
          }
      }
  
      // Return reference to the front item.
      // If there are no elements in the container, the behavior is undefined.
      const T &front() const
      {
          return v_[head_];
      }
  
      T &front()
      {
          return v_[head_];
      }
  
      // Return number of elements actually stored
      size_t size() const
      {
          if (tail_ >= head_)
          {
              return tail_ - head_;
          }
          else
          {
              return max_items_ - (head_ - tail_);
          }
      }
  
      // Return const reference to item by index.
      // If index is out of range 0â€¦size()-1, the behavior is undefined.
      const T &at(size_t i) const
      {
          assert(i < size());
          return v_[(head_ + i) % max_items_];
      }
  
      // Pop item from front.
      // If there are no elements in the container, the behavior is undefined.
      void pop_front()
      {
          head_ = (head_ + 1) % max_items_;
      }
  
      bool empty() const
      {
          return tail_ == head_;
      }
  
      bool full() const
      {
          // head is ahead of the tail by 1
          if (max_items_ > 0)
          {
              return ((tail_ + 1) % max_items_) == head_;
          }
          return false;
      }
  
      size_t overrun_counter() const
      {
          return overrun_counter_;
      }
  
  private:
      // copy from other&& and reset it to disabled state
      void copy_moveable(circular_q &&other) SPDLOG_NOEXCEPT
      {
          max_items_ = other.max_items_;
          head_ = other.head_;
          tail_ = other.tail_;
          overrun_counter_ = other.overrun_counter_;
          v_ = std::move(other.v_);
  
          // put &&other in disabled, but valid state
          other.max_items_ = 0;
          other.head_ = other.tail_ = 0;
          other.overrun_counter_ = 0;
      }
  };
  } // namespace details
  } // namespace spdlog
  