|  | /*
 * Simple Queue, specially developed for Atom types
 * Copyright (C) 2012-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 LV2_ATOM_QUEUE_HPP_INCLUDED
#define LV2_ATOM_QUEUE_HPP_INCLUDED
#include "CarlaMathUtils.hpp"
#include "CarlaMutex.hpp"
#include "CarlaRingBuffer.hpp"
#include "lv2/atom.h"
// -----------------------------------------------------------------------
class Lv2AtomRingBufferControl : public RingBufferControl<HeapBuffer>
{
public:
    Lv2AtomRingBufferControl() noexcept
        : RingBufferControl<HeapBuffer>(nullptr),
          fIsDummy(false)
    {
        fLv2Buffer.size = 0;
        fLv2Buffer.buf  = nullptr;
    }
    ~Lv2AtomRingBufferControl() noexcept
    {
        if (fLv2Buffer.buf != nullptr && ! fIsDummy)
        {
            delete[] fLv2Buffer.buf;
            fLv2Buffer.buf = nullptr;
        }
    }
    // -------------------------------------------------------------------
    void createBuffer(const uint32_t size) noexcept
    {
        if (fLv2Buffer.size == size && ! fIsDummy)
            return;
        if (fLv2Buffer.buf != nullptr)
        {
            if (! fIsDummy)
                delete[] fLv2Buffer.buf;
            fLv2Buffer.buf = nullptr;
        }
        // shouldn't really happen please...
        CARLA_SAFE_ASSERT_RETURN(size > 0,);
        const uint32_t p2size(carla_nextPowerOf2(size));
        try {
            fLv2Buffer.buf = new char[p2size];
        } CARLA_SAFE_EXCEPTION_RETURN("Lv2AtomRingBufferControl::createBuffer",);
        fLv2Buffer.size = p2size;
        setRingBuffer(&fLv2Buffer, true);
        //lockMemory();
    }
    // used for tmp buffers only
    void copyDump(HeapBuffer& rb, char dumpBuf[]) noexcept
    {
        CARLA_SAFE_ASSERT_RETURN(fLv2Buffer.size == 0,);
        CARLA_SAFE_ASSERT_RETURN(fLv2Buffer.buf == nullptr,);
        fLv2Buffer.buf  = dumpBuf;
        fLv2Buffer.size = rb.size;
        fLv2Buffer.head = rb.head;
        fLv2Buffer.tail = rb.tail;
        fLv2Buffer.written = rb.written;
        fLv2Buffer.invalidateCommit = rb.invalidateCommit;
        fIsDummy = true;
        std::memcpy(dumpBuf, rb.buf, rb.size);
        setRingBuffer(&fLv2Buffer, false);
    }
    // -------------------------------------------------------------------
    const LV2_Atom* readAtom(uint32_t* const portIndex) noexcept
    {
        fRetAtom.atom.size = 0;
        fRetAtom.atom.type = 0;
        tryRead(&fRetAtom.atom, sizeof(LV2_Atom));
        if (fRetAtom.atom.size == 0 || fRetAtom.atom.type == 0)
            return nullptr;
        CARLA_SAFE_ASSERT_RETURN(fRetAtom.atom.size < kMaxDataSize, nullptr);
        int32_t index = -1;
        tryRead(&index, sizeof(int32_t));
        if (index < 0)
            return nullptr;
        if (portIndex != nullptr)
            *portIndex = static_cast<uint32_t>(index);
        carla_zeroChar(fRetAtom.data, fRetAtom.atom.size);
        tryRead(fRetAtom.data, fRetAtom.atom.size);
        return &fRetAtom.atom;
    }
    // -------------------------------------------------------------------
    bool writeAtom(const LV2_Atom* const atom, const int32_t portIndex) noexcept
    {
        tryWrite(atom,       sizeof(LV2_Atom));
        tryWrite(&portIndex, sizeof(int32_t));
        tryWrite(LV2_ATOM_BODY_CONST(atom), atom->size);
        return commitWrite();
    }
    bool writeAtomChunk(const LV2_Atom* const atom, const void* const data, const int32_t portIndex) noexcept
    {
        tryWrite(atom,       sizeof(LV2_Atom));
        tryWrite(&portIndex, sizeof(int32_t));
        tryWrite(data,       atom->size);
        return commitWrite();
    }
    // -------------------------------------------------------------------
private:
    HeapBuffer fLv2Buffer;
    bool fIsDummy;
    static const size_t kMaxDataSize = 8192;
    struct RetAtom {
        LV2_Atom atom;
        char     data[kMaxDataSize];
    } fRetAtom;
    friend class Lv2AtomQueue;
    CARLA_PREVENT_HEAP_ALLOCATION
    CARLA_DECLARE_NON_COPY_CLASS(Lv2AtomRingBufferControl)
};
// -----------------------------------------------------------------------
class Lv2AtomQueue
{
public:
    Lv2AtomQueue() noexcept {}
    // -------------------------------------------------------------------
    void createBuffer(const uint32_t size) noexcept
    {
        fRingBufferCtrl.createBuffer(size);
    }
    // -------------------------------------------------------------------
    uint32_t getSize() const noexcept
    {
        return fRingBufferCtrl.fLv2Buffer.size;
    }
    bool isEmpty() const noexcept
    {
        return (fRingBufferCtrl.fLv2Buffer.buf == nullptr || !fRingBufferCtrl.isDataAvailable());
    }
    // must have been locked before
    bool get(const LV2_Atom** const atom, uint32_t* const portIndex) noexcept
    {
        CARLA_SAFE_ASSERT_RETURN(atom != nullptr && portIndex != nullptr, false);
        if (! fRingBufferCtrl.isDataAvailable())
            return false;
        if (const LV2_Atom* const retAtom = fRingBufferCtrl.readAtom(portIndex))
        {
            *atom = retAtom;
            return true;
        }
        return false;
    }
    // must NOT been locked, we do that here
    bool put(const LV2_Atom* const atom, const uint32_t portIndex) noexcept
    {
        CARLA_SAFE_ASSERT_RETURN(atom != nullptr && atom->size > 0, false);
        const CarlaMutexLocker cml(fMutex);
        return fRingBufferCtrl.writeAtom(atom, static_cast<int32_t>(portIndex));
    }
    // must NOT been locked, we do that here
    bool putChunk(const LV2_Atom* const atom, const void* const data, const uint32_t portIndex) noexcept
    {
        CARLA_SAFE_ASSERT_RETURN(atom != nullptr && atom->size > 0, false);
        CARLA_SAFE_ASSERT_RETURN(data != nullptr, false);
        const CarlaMutexLocker cml(fMutex);
        return fRingBufferCtrl.writeAtomChunk(atom, data, static_cast<int32_t>(portIndex));
    }
    // -------------------------------------------------------------------
    void lock() const noexcept
    {
        fMutex.lock();
    }
    bool tryLock() const noexcept
    {
        return fMutex.tryLock();
    }
    void unlock() const noexcept
    {
        fMutex.unlock();
    }
    // -------------------------------------------------------------------
    void copyDataFromQueue(Lv2AtomQueue& queue) noexcept
    {
        // lock source
        const CarlaMutexLocker cml1(queue.fMutex);
        {
            // copy data from source
            const CarlaMutexLocker cml2(fMutex);
            fRingBufferCtrl.fLv2Buffer = queue.fRingBufferCtrl.fLv2Buffer;
        }
        // clear source
        queue.fRingBufferCtrl.clear();
    }
    // used for tmp buffers only
    void copyAndDumpDataFromQueue(Lv2AtomQueue& queue, char dumpBuf[]) noexcept
    {
        // lock source
        const CarlaMutexLocker cml1(queue.fMutex);
        {
            // copy data from source
            const CarlaMutexLocker cml2(fMutex);
            fRingBufferCtrl.copyDump(queue.fRingBufferCtrl.fLv2Buffer, dumpBuf);
        }
        // clear source
        queue.fRingBufferCtrl.clear();
    }
    // -------------------------------------------------------------------
private:
    CarlaMutex fMutex;
    Lv2AtomRingBufferControl fRingBufferCtrl;
    CARLA_PREVENT_HEAP_ALLOCATION
    CARLA_DECLARE_NON_COPY_CLASS(Lv2AtomQueue)
};
// -----------------------------------------------------------------------
#endif // LV2_ATOM_QUEUE_HPP_INCLUDED
 |