|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593 |
- /*
- * Carla Ring Buffer
- * Copyright (C) 2013-2023 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 "CarlaMathUtils.hpp"
- #include "CarlaMemUtils.hpp"
-
- // --------------------------------------------------------------------------------------------------------------------
- // Buffer structs
-
- /*
- head:
- current writing position, headmost position of the buffer.
- increments when writing.
-
- tail:
- current reading position, last used position of the buffer.
- increments when reading.
- head == tail means empty buffer
-
- wrtn:
- temporary position of head until a commitWrite() is called.
- if buffer writing fails, wrtn will be back to head position thus ignoring the last operation(s).
- if buffer writing succeeds, head will be set to this variable.
-
- invalidateCommit:
- boolean used to check if a write operation failed.
- this ensures we don't get incomplete writes.
- */
-
- struct HeapBuffer {
- uint32_t size;
- uint32_t head, tail, wrtn;
- bool invalidateCommit;
- uint8_t* buf;
-
- void copyDataFrom(const HeapBuffer& rb) noexcept
- {
- CARLA_SAFE_ASSERT_RETURN(size == rb.size,);
-
- head = rb.head;
- tail = rb.tail;
- wrtn = rb.wrtn;
- invalidateCommit = rb.invalidateCommit;
- std::memcpy(buf, rb.buf, size);
- }
- };
-
- struct SmallStackBuffer {
- static constexpr const uint32_t size = 4096;
- uint32_t head, tail, wrtn;
- bool invalidateCommit;
- uint8_t buf[size];
- };
-
- struct BigStackBuffer {
- static constexpr const uint32_t size = 16384;
- uint32_t head, tail, wrtn;
- bool invalidateCommit;
- uint8_t buf[size];
- };
-
- struct HugeStackBuffer {
- static constexpr const uint32_t size = 65536;
- uint32_t head, tail, wrtn;
- bool invalidateCommit;
- uint8_t buf[size];
- };
-
- // --------------------------------------------------------------------------------------------------------------------
- // CarlaRingBufferControl templated class
-
- template <class BufferStruct>
- class CarlaRingBufferControl
- {
- public:
- CarlaRingBufferControl() noexcept
- : fBuffer(nullptr),
- fErrorReading(false),
- fErrorWriting(false) {}
-
- virtual ~CarlaRingBufferControl() noexcept {}
-
- // ----------------------------------------------------------------------------------------------------------------
-
- void clearData() noexcept
- {
- CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr,);
-
- fBuffer->head = fBuffer->tail = fBuffer->wrtn = 0;
- fBuffer->invalidateCommit = false;
-
- carla_zeroBytes(fBuffer->buf, fBuffer->size);
-
- fErrorReading = fErrorWriting = false;
- }
-
- void flush() noexcept
- {
- CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr,);
-
- fBuffer->head = fBuffer->tail = fBuffer->wrtn = 0;
- fBuffer->invalidateCommit = false;
-
- fErrorWriting = false;
- }
-
- // ----------------------------------------------------------------------------------------------------------------
-
- bool commitWrite() noexcept
- {
- CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, false);
-
- if (fBuffer->invalidateCommit)
- {
- fBuffer->wrtn = fBuffer->head;
- fBuffer->invalidateCommit = false;
- return false;
- }
-
- // nothing to commit?
- CARLA_SAFE_ASSERT_RETURN(fBuffer->head != fBuffer->wrtn, false);
-
- // all ok
- fBuffer->head = fBuffer->wrtn;
- fErrorWriting = false;
- return true;
- }
-
- bool isDataAvailableForReading() const noexcept;
-
- bool isEmpty() const noexcept
- {
- CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, false);
-
- return fBuffer->buf == nullptr || fBuffer->head == fBuffer->tail;
- }
-
- uint32_t getSize() const noexcept
- {
- return fBuffer != nullptr ? fBuffer->size : 0;
- }
-
- uint32_t getReadableDataSize() const noexcept
- {
- CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, 0);
-
- const uint32_t wrap = fBuffer->head >= fBuffer->tail ? 0 : fBuffer->size;
-
- return wrap + fBuffer->head - fBuffer->tail;
- }
-
- uint32_t getWritableDataSize() const noexcept
- {
- CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, 0);
-
- const uint32_t wrap = fBuffer->tail > fBuffer->wrtn ? 0 : fBuffer->size;
-
- return wrap + fBuffer->tail - fBuffer->wrtn - 1;
- }
-
- // ----------------------------------------------------------------------------------------------------------------
-
- bool readBool() noexcept
- {
- bool b = false;
- return tryRead(&b, sizeof(bool)) ? b : false;
- }
-
- uint8_t readByte() noexcept
- {
- uint8_t B = 0;
- return tryRead(&B, sizeof(uint8_t)) ? B : 0;
- }
-
- int16_t readShort() noexcept
- {
- int16_t s = 0;
- return tryRead(&s, sizeof(int16_t)) ? s : 0;
- }
-
- uint16_t readUShort() noexcept
- {
- uint16_t us = 0;
- return tryRead(&us, sizeof(uint16_t)) ? us : 0;
- }
-
- int32_t readInt() noexcept
- {
- int32_t i = 0;
- return tryRead(&i, sizeof(int32_t)) ? i : 0;
- }
-
- uint32_t readUInt() noexcept
- {
- uint32_t ui = 0;
- return tryRead(&ui, sizeof(int32_t)) ? ui : 0;
- }
-
- int64_t readLong() noexcept
- {
- int64_t l = 0;
- return tryRead(&l, sizeof(int64_t)) ? l : 0;
- }
-
- uint64_t readULong() noexcept
- {
- uint64_t ul = 0;
- return tryRead(&ul, sizeof(int64_t)) ? ul : 0;
- }
-
- float readFloat() noexcept
- {
- float f = 0.0f;
- return tryRead(&f, sizeof(float)) ? f : 0.0f;
- }
-
- double readDouble() noexcept
- {
- double d = 0.0;
- return tryRead(&d, sizeof(double)) ? d : 0.0;
- }
-
- void readCustomData(void* const data, const uint32_t size) noexcept
- {
- CARLA_SAFE_ASSERT_RETURN(data != nullptr,);
- CARLA_SAFE_ASSERT_RETURN(size > 0,);
-
- if (! tryRead(data, size))
- std::memset(data, 0, size);
- }
-
- template <typename T>
- void readCustomType(T& type) noexcept
- {
- if (! tryRead(&type, sizeof(T)))
- std::memset(&type, 0, sizeof(T));
- }
-
- // ----------------------------------------------------------------------------------------------------------------
-
- void skipRead(const uint32_t size) noexcept
- {
- CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr,);
- CARLA_SAFE_ASSERT_RETURN(size > 0,);
- CARLA_SAFE_ASSERT_RETURN(size < fBuffer->size,);
-
- // empty
- if (fBuffer->head == fBuffer->tail)
- return;
-
- const uint32_t head = fBuffer->head;
- const uint32_t tail = fBuffer->tail;
- const uint32_t wrap = head > tail ? 0 : fBuffer->size;
-
- if (size > wrap + head - tail)
- {
- if (! fErrorReading)
- {
- fErrorReading = true;
- carla_stderr2("CarlaRingBuffer::skipRead(%u): failed, not enough space", size);
- }
- return;
- }
-
- uint32_t readto = tail + size;
-
- if (readto >= fBuffer->size)
- readto -= fBuffer->size;
-
- fBuffer->tail = readto;
- fErrorReading = false;
- return;
- }
-
- // ----------------------------------------------------------------------------------------------------------------
-
- bool writeBool(const bool value) noexcept
- {
- return tryWrite(&value, sizeof(bool));
- }
-
- bool writeByte(const uint8_t value) noexcept
- {
- return tryWrite(&value, sizeof(uint8_t));
- }
-
- bool writeShort(const int16_t value) noexcept
- {
- return tryWrite(&value, sizeof(int16_t));
- }
-
- bool writeUShort(const uint16_t value) noexcept
- {
- return tryWrite(&value, sizeof(uint16_t));
- }
-
- bool writeInt(const int32_t value) noexcept
- {
- return tryWrite(&value, sizeof(int32_t));
- }
-
- bool writeUInt(const uint32_t value) noexcept
- {
- return tryWrite(&value, sizeof(uint32_t));
- }
-
- bool writeLong(const int64_t value) noexcept
- {
- return tryWrite(&value, sizeof(int64_t));
- }
-
- bool writeULong(const uint64_t value) noexcept
- {
- return tryWrite(&value, sizeof(uint64_t));
- }
-
- bool writeFloat(const float value) noexcept
- {
- return tryWrite(&value, sizeof(float));
- }
-
- bool writeDouble(const double value) noexcept
- {
- return tryWrite(&value, sizeof(double));
- }
-
- bool writeCustomData(const void* const data, const uint32_t size) noexcept
- {
- CARLA_SAFE_ASSERT_RETURN(data != nullptr, false);
- CARLA_SAFE_ASSERT_RETURN(size > 0, false);
-
- return tryWrite(data, size);
- }
-
- template <typename T>
- bool writeCustomType(const T& type) noexcept
- {
- return tryWrite(&type, sizeof(T));
- }
-
- // ----------------------------------------------------------------------------------------------------------------
-
- protected:
- void setRingBuffer(BufferStruct* const ringBuf, const bool resetBuffer) noexcept
- {
- CARLA_SAFE_ASSERT_RETURN(fBuffer != ringBuf,);
-
- fBuffer = ringBuf;
-
- if (resetBuffer && ringBuf != nullptr)
- clearData();
- }
-
- // ----------------------------------------------------------------------------------------------------------------
-
- bool tryRead(void* const buf, const uint32_t size) noexcept
- {
- CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, false);
- #if defined(__clang__)
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wtautological-pointer-compare"
- #endif
- CARLA_SAFE_ASSERT_RETURN(fBuffer->buf != nullptr, false);
- #if defined(__clang__)
- #pragma clang diagnostic pop
- #endif
- CARLA_SAFE_ASSERT_RETURN(buf != nullptr, false);
- CARLA_SAFE_ASSERT_RETURN(size > 0, false);
- CARLA_SAFE_ASSERT_RETURN(size < fBuffer->size, false);
-
- // empty
- if (fBuffer->head == fBuffer->tail)
- return false;
-
- uint8_t* const bytebuf = static_cast<uint8_t*>(buf);
-
- const uint32_t head = fBuffer->head;
- const uint32_t tail = fBuffer->tail;
- const uint32_t wrap = head > tail ? 0 : fBuffer->size;
-
- if (size > wrap + head - tail)
- {
- if (! fErrorReading)
- {
- fErrorReading = true;
- carla_stderr2("CarlaRingBuffer::tryRead(%p, %u): failed, not enough space", buf, size);
- }
- return false;
- }
-
- uint32_t readto = tail + size;
-
- if (readto > fBuffer->size)
- {
- readto -= fBuffer->size;
-
- if (size == 1)
- {
- std::memcpy(bytebuf, fBuffer->buf + tail, 1);
- }
- else
- {
- const uint32_t firstpart = fBuffer->size - tail;
- std::memcpy(bytebuf, fBuffer->buf + tail, firstpart);
- std::memcpy(bytebuf + firstpart, fBuffer->buf, readto);
- }
- }
- else
- {
- std::memcpy(bytebuf, fBuffer->buf + tail, size);
-
- if (readto == fBuffer->size)
- readto = 0;
- }
-
- fBuffer->tail = readto;
- fErrorReading = false;
- return true;
- }
-
- bool tryWrite(const void* const buf, const uint32_t size) noexcept
- {
- CARLA_SAFE_ASSERT_RETURN(fBuffer != nullptr, false);
- CARLA_SAFE_ASSERT_RETURN(buf != nullptr, false);
- CARLA_SAFE_ASSERT_RETURN(size > 0, false);
- CARLA_SAFE_ASSERT_UINT2_RETURN(size < fBuffer->size, size, fBuffer->size, false);
-
- const uint8_t* const bytebuf = static_cast<const uint8_t*>(buf);
-
- const uint32_t tail = fBuffer->tail;
- const uint32_t wrtn = fBuffer->wrtn;
- const uint32_t wrap = tail > wrtn ? 0 : fBuffer->size;
-
- if (size >= wrap + tail - wrtn)
- {
- if (! fErrorWriting)
- {
- fErrorWriting = true;
- carla_stderr2("CarlaRingBuffer::tryWrite(%p, %u): failed, not enough space", buf, size);
- }
- fBuffer->invalidateCommit = true;
- return false;
- }
-
- uint32_t writeto = wrtn + size;
-
- if (writeto > fBuffer->size)
- {
- writeto -= fBuffer->size;
-
- if (size == 1)
- {
- std::memcpy(fBuffer->buf, bytebuf, 1);
- }
- else
- {
- const uint32_t firstpart = fBuffer->size - wrtn;
- std::memcpy(fBuffer->buf + wrtn, bytebuf, firstpart);
- std::memcpy(fBuffer->buf, bytebuf + firstpart, writeto);
- }
- }
- else
- {
- std::memcpy(fBuffer->buf + wrtn, bytebuf, size);
-
- if (writeto == fBuffer->size)
- writeto = 0;
- }
-
- fBuffer->wrtn = writeto;
- return true;
- }
-
- private:
- BufferStruct* fBuffer;
-
- // wherever read/write errors have been printed to terminal
- bool fErrorReading;
- bool fErrorWriting;
-
- CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
- CARLA_DECLARE_NON_COPYABLE(CarlaRingBufferControl)
- };
-
- template <class BufferStruct>
- inline bool CarlaRingBufferControl<BufferStruct>::isDataAvailableForReading() const noexcept
- {
- return (fBuffer != nullptr && fBuffer->head != fBuffer->tail);
- }
-
- template <>
- inline bool CarlaRingBufferControl<HeapBuffer>::isDataAvailableForReading() const noexcept
- {
- return (fBuffer != nullptr && fBuffer->buf != nullptr && fBuffer->head != fBuffer->tail);
- }
-
- // --------------------------------------------------------------------------------------------------------------------
- // CarlaRingBuffer using heap space
-
- class CarlaHeapRingBuffer : public CarlaRingBufferControl<HeapBuffer>
- {
- public:
- CarlaHeapRingBuffer() noexcept
- : fHeapBuffer{0, 0, 0, 0, false, nullptr} {}
-
- ~CarlaHeapRingBuffer() noexcept override
- {
- if (fHeapBuffer.buf == nullptr)
- 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(size > 0,);
-
- const uint32_t p2size = carla_nextPowerOf2(size);
-
- try {
- fHeapBuffer.buf = new uint8_t[p2size];
- } CARLA_SAFE_EXCEPTION_RETURN("CarlaHeapRingBuffer::createBuffer",);
-
- fHeapBuffer.size = p2size;
- setRingBuffer(&fHeapBuffer, true);
-
- if (mlock)
- {
- carla_mlock(&fHeapBuffer, sizeof(fHeapBuffer));
- carla_mlock(fHeapBuffer.buf, p2size);
- }
- }
-
- void deleteBuffer() noexcept
- {
- if (fHeapBuffer.buf == nullptr)
- return;
-
- setRingBuffer(nullptr, false);
-
- delete[] fHeapBuffer.buf;
- fHeapBuffer.buf = nullptr;
- fHeapBuffer.size = 0;
- }
-
- private:
- HeapBuffer fHeapBuffer;
-
- CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
- CARLA_DECLARE_NON_COPYABLE(CarlaHeapRingBuffer)
- };
-
- // --------------------------------------------------------------------------------------------------------------------
- // CarlaRingBuffer using small stack space
-
- class CarlaSmallStackRingBuffer : public CarlaRingBufferControl<SmallStackBuffer>
- {
- public:
- CarlaSmallStackRingBuffer() noexcept
- : fStackBuffer{0, 0, 0, false, {}}
- {
- setRingBuffer(&fStackBuffer, true);
- }
-
- private:
- SmallStackBuffer fStackBuffer;
-
- CARLA_PREVENT_VIRTUAL_HEAP_ALLOCATION
- CARLA_DECLARE_NON_COPYABLE(CarlaSmallStackRingBuffer)
- };
-
- // --------------------------------------------------------------------------------------------------------------------
-
- #endif // CARLA_RING_BUFFER_HPP_INCLUDED
|