// (C) 2020 Philip Endecott.
// Distributed under the Boost Software License, Version 1.0.
// See accompanying file LICENCE.txt or copy at https://www.boost.org/LICENSE_1_0.txt

#ifndef circular_buffer_hh
#define circular_buffer_hh

#include <array>

// Simple fixed-capacity circular buffer.
// This is not suitable for types with non-trivial ctors and dtors, as
// the underlying array is default-initialised and popped items are not
// destroyed.
// This is not lock-free.
// We only need it to work in a producer-consumer safe way; how can we
// do that?

template <typename T, size_t N>
class circular_buffer
{
  std::array<T,N> data;
  T* volatile front_p;
  T* volatile back_p;
// If we were to store indexes, we could use smaller types and we would
// hopefully get a trivial ctor.

public:

  circular_buffer():
    front_p(data.data()),
    back_p(data.data())
  {}

  size_t capacity() const { return N-1; }
  bool   empty()    const { return front_p == back_p; }
  size_t size()     const { return (back_p+N-front_p) % N; }
  bool   full()     const { return size() == capacity(); }

  void push_back(T value)  // Precondition: !full()
  {
    *back_p = value;
    ++back_p;
    if (back_p - data.data() == N) back_p = data.data();
  }

  const T& front() const { return *front_p; }  // Precondition: !empty()
  T&       front()       { return *front_p; }

  void pop_front()  // Precondition: !empty()
  {
    ++front_p;
    if (front_p - data.data() == N) front_p = data.data();
  }
};


#endif

