Program Listing for File CircularBuffer.h

Program Listing for File CircularBuffer.h#

Return to documentation for file (src/buffer/CircularBuffer.h)

#pragma once

/* toolchain */
#include <array>
#include <cassert>
#include <cstring>

/* internal */
#include "../generated/ifgen/common.h"
#include "../generated/structs/BufferState.h"

namespace Coral
{

template <std::size_t depth, typename element_t = std::byte,
          std::size_t alignment = sizeof(element_t)>
class CircularBuffer
{
    static_assert(depth > 0);
    static_assert(std::has_single_bit(alignment));

  public:
    static constexpr std::size_t Depth = depth;

    CircularBuffer() : buffer(), state()
    {
    }

    template <std::endian endianness, byte_size T>
    inline std::size_t write(T elem)
        requires byte_size<element_t>
    {
        return write_single(static_cast<element_t>(elem));
    }

    template <std::endian endianness, typename T>
    inline std::size_t write(T elem)
        requires byte_size<element_t> && (not byte_size<T>)
    {
        /* Parameter passed by value can be directly swapped. */
        elem = handle_endian<endianness>(elem);
        return write_n(reinterpret_cast<const element_t *>(&elem), sizeof(T));
    }

    template <std::endian endianness, ifgen_struct T>
    inline std::size_t write(const T *elem)
        requires byte_size<element_t>
    {
        /* Use a stack instance/copy for byte swapping. */
        T temp = T();
        temp.template decode<endianness>(elem->raw_ro());
        return write_n(reinterpret_cast<const element_t *>(temp.raw_ro()),
                       T::size);
    }

    template <std::endian endianness, byte_size T>
    inline T read(void)
        requires byte_size<element_t>
    {
        return static_cast<T>(read_single());
    }

    template <std::endian endianness, typename T>
    inline T read(void)
        requires byte_size<element_t> && (not byte_size<T>)
    {
        T result = T();
        read_n(reinterpret_cast<element_t *>(&result), sizeof(T));
        return handle_endian<endianness>(result);
    }

    template <std::endian endianness, ifgen_struct T>
    inline void read(T *elem)
        requires byte_size<element_t>
    {
        read_n(reinterpret_cast<element_t *>(elem->raw()), T::size);
        elem->template endian<endianness>();
    }

    inline std::size_t write_single(const element_t elem)
    {
        buffer[write_index()] = elem;
        state.write_cursor++;

        state.write_count++;
        return 1;
    }

    inline std::size_t write_n(const element_t *elem_array, std::size_t count)
    {
        std::size_t max_contiguous;
        std::size_t to_write;

        assert(count > 0);

        while (count)
        {
            /*
             * We can only write from the current index to the end of the
             * underlying, linear buffer.
             */
            max_contiguous = depth - write_index();
            to_write = std::min(max_contiguous, count);

            /* Copy the bytes (elements -> buffer). */
            if (elem_array)
            {
                std::memcpy(&(buffer.data()[write_index()]), elem_array,
                            to_write * sizeof(element_t));
                elem_array += to_write;
            }

            count -= to_write;
            state.write_cursor += to_write;

            state.write_count += to_write;
        }

        return count;
    }

    inline element_t peek(void)
    {
        return buffer[read_index()];
    }

    inline void read_single(element_t &elem)
    {
        elem = peek();
        state.read_cursor++;

        state.read_count++;
    }

    inline element_t read_single(void)
    {
        element_t elem;
        read_single(elem);
        return elem;
    }

    inline void read_n(element_t *elem_array, std::size_t count)
    {
        std::size_t max_contiguous;
        std::size_t to_read;

        assert(count > 0);

        while (count)
        {
            /*
             * We can only read from the current index to the end of the
             * underlying, linear buffer.
             */
            max_contiguous = depth - read_index();
            to_read = std::min(max_contiguous, count);

            /* Copy the bytes (buffer -> elements). */
            if (elem_array)
            {
                std::memcpy(elem_array, &(buffer.data()[read_index()]),
                            to_read * sizeof(element_t));
                elem_array += to_read;
            }

            count -= to_read;
            state.read_cursor += to_read;

            state.read_count += to_read;
        }
    }

    inline void poll_metrics(uint32_t &_read_count, uint32_t &_write_count,
                             bool reset = true)
    {
        _read_count = read_count(reset);
        _write_count = write_count(reset);
    }

    inline uint32_t write_count(bool reset = true)
    {
        auto result = state.write_count;
        if (reset)
        {
            state.write_count = 0;
        }
        return result;
    }

    inline uint32_t read_count(bool reset = true)
    {
        auto result = state.read_count;
        if (reset)
        {
            state.read_count = 0;
        }
        return result;
    }

    inline void reset(void)
    {
        state = {};
    }

    inline const element_t *head(void)
    {
        return buffer.data();
    }

  protected:
    alignas(alignment) std::array<element_t, depth> buffer;

    BufferState state;

    inline std::size_t write_index(void)
    {
        return state.write_cursor % depth;
    }

    inline std::size_t read_index(void)
    {
        return state.read_cursor % depth;
    }
};

}; // namespace Coral