/* * LV2 Atom Ring Buffer * Copyright (C) 2012-2023 Filipe Coelho * * 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_RING_BUFFER_HPP_INCLUDED #define LV2_ATOM_RING_BUFFER_HPP_INCLUDED #include "CarlaMutex.hpp" #include "CarlaRingBuffer.hpp" #include "lv2/atom.h" // -------------------------------------------------------------------------------------------------------------------- class Lv2AtomRingBuffer : public CarlaRingBufferControl { public: Lv2AtomRingBuffer() noexcept : fMutex(), fHeapBuffer{0, 0, 0, 0, false, nullptr}, fNeedsDataDelete(true) { } Lv2AtomRingBuffer(Lv2AtomRingBuffer& ringBuf, uint8_t buf[]) noexcept : fMutex(), fHeapBuffer{0, 0, 0, 0, false, nullptr}, fNeedsDataDelete(false) { fHeapBuffer.buf = buf; fHeapBuffer.size = ringBuf.fHeapBuffer.size; { const CarlaMutexLocker cml(ringBuf.fMutex); fHeapBuffer.copyDataFrom(ringBuf.fHeapBuffer); ringBuf.clearData(); } setRingBuffer(&fHeapBuffer, false); } ~Lv2AtomRingBuffer() noexcept { if (fHeapBuffer.buf == nullptr || ! fNeedsDataDelete) return; delete[] fHeapBuffer.buf; fHeapBuffer.buf = nullptr; } // ---------------------------------------------------------------------------------------------------------------- void createBuffer(const uint32_t size, const bool mlock) noexcept { CARLA_SAFE_ASSERT_RETURN(fHeapBuffer.buf == nullptr,); CARLA_SAFE_ASSERT_RETURN(fNeedsDataDelete,); CARLA_SAFE_ASSERT_RETURN(size > 0,); const uint32_t p2size = carla_nextPowerOf2(size); try { fHeapBuffer.buf = new uint8_t[p2size]; } CARLA_SAFE_EXCEPTION_RETURN("Lv2AtomRingBuffer::createBuffer",); fHeapBuffer.size = p2size; setRingBuffer(&fHeapBuffer, true); if (mlock) { carla_mlock(&fHeapBuffer, sizeof(fHeapBuffer)); carla_mlock(fHeapBuffer.buf, p2size); } } void deleteBuffer() noexcept { CARLA_SAFE_ASSERT_RETURN(fHeapBuffer.buf != nullptr,); CARLA_SAFE_ASSERT_RETURN(fNeedsDataDelete,); setRingBuffer(nullptr, false); delete[] fHeapBuffer.buf; fHeapBuffer.buf = nullptr; fHeapBuffer.size = 0; } uint32_t getSize() const noexcept { return fHeapBuffer.size; } // ---------------------------------------------------------------------------------------------------------------- bool tryLock() const noexcept { return fMutex.tryLock(); } void unlock() const noexcept { fMutex.unlock(); } // ---------------------------------------------------------------------------------------------------------------- // NOTE: must have been locked before bool get(uint32_t& portIndex, LV2_Atom* const retAtom) noexcept { if (readAtom(portIndex, retAtom)) return true; retAtom->size = retAtom->type = 0; return false; } // NOTE: 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 writeAtom(atom, static_cast(portIndex)); } // NOTE: 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 writeAtomChunk(atom, data, static_cast(portIndex)); } protected: // ---------------------------------------------------------------------------------------------------------------- bool readAtom(uint32_t& portIndex, LV2_Atom* const retAtom) noexcept { const uint32_t maxAtomSize = retAtom->size - sizeof(LV2_Atom); LV2_Atom atom = {}; if (! tryRead(&atom, sizeof(LV2_Atom))) return false; if (atom.size == 0 || atom.type == 0) return false; CARLA_SAFE_ASSERT_UINT2_RETURN(atom.size < maxAtomSize, atom.size, maxAtomSize, false); int32_t index = -1; if (! tryRead(&index, sizeof(int32_t))) return false; if (index < 0) return false; if (! tryRead(retAtom + 1, atom.size)) return false; portIndex = static_cast(index); retAtom->size = atom.size; retAtom->type = atom.type; return true; } // ---------------------------------------------------------------------------------------------------------------- bool writeAtom(const LV2_Atom* const atom, const int32_t portIndex) noexcept { if (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 { if (tryWrite(atom, sizeof(LV2_Atom)) && tryWrite(&portIndex, sizeof(int32_t))) tryWrite(data, atom->size); return commitWrite(); } // ---------------------------------------------------------------------------------------------------------------- private: CarlaMutex fMutex; HeapBuffer fHeapBuffer; const bool fNeedsDataDelete; friend class Lv2AtomQueue; CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION CARLA_DECLARE_NON_COPYABLE(Lv2AtomRingBuffer) }; // -------------------------------------------------------------------------------------------------------------------- #endif // LV2_ATOM_RING_BUFFER_HPP_INCLUDED