/* * Carla Ring Buffer * Copyright (C) 2013-2014 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 CARLA_RING_BUFFER_HPP_INCLUDED #define CARLA_RING_BUFFER_HPP_INCLUDED #include "CarlaUtils.hpp" #if 0 //ndef CARLA_OS_WIN # include # ifdef CARLA_OS_MAC # include # 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 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(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(buf)); const size_t head(static_cast(fBuffer->head)); const size_t tail(static_cast(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(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(buf)); const size_t tail(static_cast(fBuffer->tail)); const size_t wrtn(static_cast(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(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