|  | /*
 * Carla Ring Buffer
 * Copyright (C) 2013-2014 Filipe Coelho <falktx@falktx.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * For a full copy of the GNU General Public License see the doc/GPL.txt file.
 */
#ifndef CARLA_RING_BUFFER_HPP_INCLUDED
#define CARLA_RING_BUFFER_HPP_INCLUDED
#include "CarlaUtils.hpp"
#if 0 //ndef CARLA_OS_WIN
# include <sys/mman.h>
# ifdef CARLA_OS_MAC
#  include <libkern/OSAtomic.h>
# endif
#endif
// -----------------------------------------------------------------------
// RingBuffer structs
struct HeapBuffer {
    uint32_t size;
    int32_t  head, tail, written;
    bool     invalidateCommit;
    char*    buf;
    HeapBuffer& operator=(const HeapBuffer& rb) noexcept
    {
        CARLA_SAFE_ASSERT_RETURN(size == rb.size, *this);
        size = rb.size;
        head = rb.head;
        tail = rb.tail;
        written = rb.written;
        invalidateCommit = rb.invalidateCommit;
        std::memcpy(buf, rb.buf, size);
        return *this;
    }
};
struct StackBuffer {
    static const uint32_t size = 4096;
    int32_t head, tail, written;
    bool    invalidateCommit;
    char    buf[size];
};
// -----------------------------------------------------------------------
// RingBufferControl templated class
template <class BufferStruct>
class RingBufferControl
{
public:
    RingBufferControl(BufferStruct* const ringBuf) noexcept
        : fBuffer(ringBuf)
    {
        if (ringBuf != nullptr)
            clear();
    }
    void clear() noexcept
    {
        CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr,);
        fBuffer->head = 0;
        fBuffer->tail = 0;
        fBuffer->written = 0;
        fBuffer->invalidateCommit = false;
        if (fBuffer->size > 0)
            carla_zeroChar(fBuffer->buf, fBuffer->size);
    }
    void setRingBuffer(BufferStruct* const ringBuf, const bool reset) noexcept
    {
        CARLA_SAFE_ASSERT_RETURN(ringBuf != nullptr,);
        CARLA_SAFE_ASSERT_RETURN(ringBuf != fBuffer,);
        fBuffer = ringBuf;
        if (reset)
            clear();
    }
    // -------------------------------------------------------------------
    bool commitWrite() noexcept
    {
        CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, false);
        if (fBuffer->invalidateCommit)
        {
            memoryBarrier();
            fBuffer->written = fBuffer->head;
            fBuffer->invalidateCommit = false;
            return false;
        }
        else
        {
            fBuffer->head = fBuffer->written;
            return true;
        }
    }
    bool isDataAvailable() const noexcept
    {
        CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, false);
        return (fBuffer->head != fBuffer->tail);
    }
#if 0
    void lockMemory() const noexcept
    {
        CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr,);
#ifdef CARLA_OS_WIN
        try {
            ::VirtualLock(fBuffer, sizeof(BufferStruct));
            ::VirtualLock(fBuffer->buf, fBuffer->size);
        } CARLA_SAFE_EXCEPTION("RingBufferControl::lockMemory");
#else
        ::mlock(fBuffer, sizeof(BufferStruct));
        ::mlock(fBuffer->buf, fBuffer->size);
#endif
    }
#endif
    // -------------------------------------------------------------------
    char readChar() noexcept
    {
        char c = '\0';
        tryRead(&c, sizeof(char));
        return c;
    }
    int32_t readInt() noexcept
    {
        int32_t i = 0;
        tryRead(&i, sizeof(int32_t));
        return i;
    }
    uint32_t readUInt() noexcept
    {
        int32_t i = -1;
        tryRead(&i, sizeof(int32_t));
        return (i >= 0) ? static_cast<uint32_t>(i) : 0;
    }
    int64_t readLong() noexcept
    {
        int64_t l = 0;
        tryRead(&l, sizeof(int64_t));
        return l;
    }
    float readFloat() noexcept
    {
        float f = 0.0f;
        tryRead(&f, sizeof(float));
        return f;
    }
    // -------------------------------------------------------------------
    void writeChar(const char value) noexcept
    {
        tryWrite(&value, sizeof(char));
    }
    void writeInt(const int32_t value) noexcept
    {
        tryWrite(&value, sizeof(int32_t));
    }
    void writeLong(const int64_t value) noexcept
    {
        tryWrite(&value, sizeof(int64_t));
    }
    void writeFloat(const float value) noexcept
    {
        tryWrite(&value, sizeof(float));
    }
    // -------------------------------------------------------------------
protected:
    void tryRead(void* const buf, const size_t size) noexcept
    {
        CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr,);
        CARLA_SAFE_ASSERT_RETURN(buf != nullptr,);
        CARLA_SAFE_ASSERT_RETURN(size != 0,);
        CARLA_SAFE_ASSERT_RETURN(size < fBuffer->size,);
        // this should not happen
        CARLA_ASSERT(fBuffer->head >= 0);
        CARLA_ASSERT(fBuffer->tail >= 0);
        CARLA_ASSERT(fBuffer->written >= 0);
        // empty
        if (fBuffer->head == fBuffer->tail)
            return;
        char* const charbuf(static_cast<char*>(buf));
        const size_t head(static_cast<size_t>(fBuffer->head));
        const size_t tail(static_cast<size_t>(fBuffer->tail));
        const size_t wrap((head < tail) ? fBuffer->size : 0);
        if (head - tail + wrap < size)
        {
            carla_stderr2("RingBufferControl::tryRead() - failed");
            return;
        }
        size_t readto = tail + size;
        if (readto >= fBuffer->size)
        {
            readto -= fBuffer->size;
            const size_t firstpart(fBuffer->size - tail);
            std::memcpy(charbuf, fBuffer->buf + tail, firstpart);
            std::memcpy(charbuf + firstpart, fBuffer->buf, readto);
        }
        else
        {
            std::memcpy(charbuf, fBuffer->buf + tail, size);
        }
        memoryBarrier();
        fBuffer->tail = static_cast<int32_t>(readto);
    }
    void tryWrite(const void* const buf, const size_t size) noexcept
    {
        CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr,);
        CARLA_SAFE_ASSERT_RETURN(buf != nullptr,);
        CARLA_SAFE_ASSERT_RETURN(size != 0,);
        CARLA_SAFE_ASSERT_RETURN(size < fBuffer->size,);
        // this should not happen
        CARLA_ASSERT(fBuffer->head >= 0);
        CARLA_ASSERT(fBuffer->tail >= 0);
        CARLA_ASSERT(fBuffer->written >= 0);
        const char* const charbuf(static_cast<const char*>(buf));
        const size_t tail(static_cast<size_t>(fBuffer->tail));
        const size_t wrtn(static_cast<size_t>(fBuffer->written));
        const size_t wrap((tail <= wrtn) ? fBuffer->size : 0);
        if (tail - wrtn + wrap <= size)
        {
            carla_stderr2("RingBufferControl::tryWrite() - buffer full!");
            fBuffer->invalidateCommit = true;
            return;
        }
        size_t writeto = wrtn + size;
        if (writeto >= fBuffer->size)
        {
            writeto -= fBuffer->size;
            const size_t firstpart(fBuffer->size - wrtn);
            std::memcpy(fBuffer->buf + wrtn, charbuf, firstpart);
            std::memcpy(fBuffer->buf, charbuf + firstpart, writeto);
        }
        else
        {
            std::memcpy(fBuffer->buf + wrtn, charbuf, size);
        }
        memoryBarrier();
        fBuffer->written = static_cast<int32_t>(writeto);
    }
private:
    BufferStruct* fBuffer;
    static void memoryBarrier() noexcept
    {
        // this breaks win32<=>linux plugin bridges
#if 0
        try {
#if defined(CARLA_OS_MAC)
            ::OSMemoryBarrier();
#elif defined(CARLA_OS_WIN)
            ::MemoryBarrier();
#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 401
            ::__sync_synchronize();
#endif
        } CARLA_SAFE_EXCEPTION("RingBufferControl::memoryBarrier");
#endif
    }
    CARLA_PREVENT_HEAP_ALLOCATION
    CARLA_DECLARE_NON_COPY_CLASS(RingBufferControl)
};
// -----------------------------------------------------------------------
#endif // CARLA_RING_BUFFER_HPP_INCLUDED
 |