DPF

DISTRHO Plugin Framework
Log | Files | Refs | Submodules | README | LICENSE

RingBuffer.hpp (25459B)


      1 /*
      2  * DISTRHO Plugin Framework (DPF)
      3  * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
      4  *
      5  * Permission to use, copy, modify, and/or distribute this software for any purpose with
      6  * or without fee is hereby granted, provided that the above copyright notice and this
      7  * permission notice appear in all copies.
      8  *
      9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
     10  * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
     11  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
     12  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
     13  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
     14  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     15  */
     16 
     17 #ifndef DISTRHO_RING_BUFFER_HPP_INCLUDED
     18 #define DISTRHO_RING_BUFFER_HPP_INCLUDED
     19 
     20 #include "../DistrhoUtils.hpp"
     21 
     22 START_NAMESPACE_DISTRHO
     23 
     24 // -----------------------------------------------------------------------
     25 // Buffer structs
     26 
     27 /**
     28    Base structure for all RingBuffer containers.
     29    This struct details the data model used in DPF's RingBuffer class.
     30 
     31    DPF RingBuffer uses a struct just like this one to store positions, buffer data, size, etc.
     32    The RingBuffer itself takes ownership of this struct and uses it to store any needed data.
     33    This allows to dynamically change the way its ring buffer is allocated, simply by changing the template type.
     34    For example, `RingBufferControl<HeapBuffer>` will create a ring buffer with heap memory, which can be of any size.
     35    In the same vein, `RingBufferControl<SmallStackBuffer>` will create a ring buffer with stack memory,
     36    directly tied to the RingBufferControl it belongs to.
     37 
     38    The main idea behind this model is to allow RingBufferControl over memory created elsewhere,
     39    for example shared memory area.
     40    One can create/place the Buffer struct in shared memory, and point RingBufferControl to it,
     41    thus avoiding the pitfalls of sharing access to a non trivially-copyable/POD C++ class.
     42 
     43    Unlike other ring buffers, an extra variable is used to track pending writes.
     44    This is so we can write a few bytes at a time and later mark the whole operation as complete,
     45    thus avoiding the issue of reading data too early from the other side.
     46    For example, write the size of some data first, and then the actual data.
     47    The reading side will only see data available once size + data is completely written and "committed".
     48  */
     49 struct HeapBuffer {
     50    /**
     51       Size of the buffer, allocated in @a buf.
     52       If the size is fixed (stack buffer), this variable can be static.
     53     */
     54     uint32_t size;
     55 
     56    /**
     57       Current writing position, headmost position of the buffer.
     58       Increments when writing.
     59     */
     60     uint32_t head;
     61 
     62    /**
     63       Current reading position, last used position of the buffer.
     64       Increments when reading.
     65       head == tail means empty buffer.
     66     */
     67     uint32_t tail;
     68 
     69    /**
     70       Temporary position of head until a commitWrite() is called.
     71       If buffer writing fails, wrtn will be back to head position thus ignoring the last operation(s).
     72       If buffer writing succeeds, head will be set to this variable.
     73     */
     74     uint32_t wrtn;
     75 
     76    /**
     77       Boolean used to check if a write operation failed.
     78       This ensures we don't get incomplete writes.
     79     */
     80     bool invalidateCommit;
     81 
     82    /**
     83       Pointer to buffer data.
     84       This can be either stack or heap data, depending on the usecase.
     85     */
     86     uint8_t* buf;
     87 };
     88 
     89 /**
     90    RingBufferControl compatible struct with a relatively small stack size (4k bytes).
     91    @see HeapBuffer
     92 */
     93 struct SmallStackBuffer {
     94     static const uint32_t size = 4096;
     95     uint32_t head, tail, wrtn;
     96     bool     invalidateCommit;
     97     uint8_t  buf[size];
     98 };
     99 
    100 /**
    101    RingBufferControl compatible struct with a relatively big stack size (16k bytes).
    102    @see HeapBuffer
    103 */
    104 struct BigStackBuffer {
    105     static const uint32_t size = 16384;
    106     uint32_t head, tail, wrtn;
    107     bool     invalidateCommit;
    108     uint8_t  buf[size];
    109 };
    110 
    111 /**
    112    RingBufferControl compatible struct with a huge stack size (64k bytes).
    113    @see HeapBuffer
    114 */
    115 struct HugeStackBuffer {
    116     static const uint32_t size = 65536;
    117     uint32_t head, tail, wrtn;
    118     bool     invalidateCommit;
    119     uint8_t  buf[size];
    120 };
    121 
    122 #ifdef DISTRHO_PROPER_CPP11_SUPPORT
    123 # define HeapBuffer_INIT  {0, 0, 0, 0, false, nullptr}
    124 # define StackBuffer_INIT {0, 0, 0, false, {0}}
    125 #else
    126 # define HeapBuffer_INIT
    127 # define StackBuffer_INIT
    128 #endif
    129 
    130 // -----------------------------------------------------------------------
    131 // RingBufferControl templated class
    132 
    133 /**
    134    DPF built-in RingBuffer class.
    135    RingBufferControl takes one buffer struct to take control over, and operates over it.
    136 
    137    This is meant for single-writer, single-reader type of control.
    138    Writing and reading is wait and lock-free.
    139 
    140    Typically usage involves:
    141    ```
    142    // definition
    143    HeapRingBuffer myHeapBuffer; // or RingBufferControl<HeapBuffer> class for more control
    144 
    145    // construction, only needed for heap buffers
    146    myHeapBuffer.createBuffer(8192);
    147 
    148    // writing data
    149    myHeapBuffer.writeUInt(size);
    150    myHeapBuffer.writeCustomData(someOtherData, size);
    151    myHeapBuffer.commitWrite();
    152 
    153    // reading data
    154    if (myHeapBuffer.isDataAvailableForReading())
    155    {
    156       uint32_t size;
    157       if (myHeapBuffer.readUInt(size) && readCustomData(&anotherData, size))
    158       {
    159           // do something with "anotherData"
    160       }
    161    }
    162    ```
    163 
    164    @see HeapBuffer
    165  */
    166 template <class BufferStruct>
    167 class RingBufferControl
    168 {
    169 public:
    170     /*
    171      * Constructor for uninitialised ring buffer.
    172      * A call to setRingBuffer is required to tied this control to a ring buffer struct;
    173      *
    174      */
    175     RingBufferControl() noexcept
    176         : buffer(nullptr),
    177           errorReading(false),
    178           errorWriting(false) {}
    179 
    180     /*
    181      * Destructor.
    182      */
    183     virtual ~RingBufferControl() noexcept {}
    184 
    185     // -------------------------------------------------------------------
    186     // check operations
    187 
    188     /*
    189      * Check if there is any data available for reading, regardless of size.
    190      */
    191     bool isDataAvailableForReading() const noexcept;
    192 
    193     /*
    194      * Check if ring buffer is empty (that is, there is nothing to read).
    195      */
    196     bool isEmpty() const noexcept
    197     {
    198         DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr, false);
    199 
    200         return (buffer->buf == nullptr || buffer->head == buffer->tail);
    201     }
    202 
    203     /*
    204      * Get the full ringbuffer size.
    205      */
    206     uint32_t getSize() const noexcept
    207     {
    208         return buffer != nullptr ? buffer->size : 0;
    209     }
    210 
    211     /*
    212      * Get the size of the data available to read.
    213      */
    214     uint32_t getReadableDataSize() const noexcept
    215     {
    216         DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr, 0);
    217 
    218         const uint32_t wrap = buffer->head >= buffer->tail ? 0 : buffer->size;
    219 
    220         return wrap + buffer->head - buffer->tail;
    221     }
    222 
    223     /*
    224      * Get the size of the data available to write.
    225      */
    226     uint32_t getWritableDataSize() const noexcept
    227     {
    228         DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr, 0);
    229 
    230         const uint32_t wrap = buffer->tail > buffer->wrtn ? 0 : buffer->size;
    231 
    232         return wrap + buffer->tail - buffer->wrtn - 1;
    233     }
    234 
    235     // -------------------------------------------------------------------
    236     // clear/reset operations
    237 
    238     /*
    239      * Clear the entire ring buffer data, marking the buffer as empty.
    240      * Requires a buffer struct tied to this class.
    241      */
    242     void clearData() noexcept
    243     {
    244         DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr,);
    245 
    246         buffer->head = 0;
    247         buffer->tail = 0;
    248         buffer->wrtn = 0;
    249         buffer->invalidateCommit = false;
    250 
    251         std::memset(buffer->buf, 0, buffer->size);
    252     }
    253 
    254     /*
    255      * Reset the ring buffer read and write positions, marking the buffer as empty.
    256      * Requires a buffer struct tied to this class.
    257      */
    258     void flush() noexcept
    259     {
    260         DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr,);
    261 
    262         buffer->head = buffer->tail = buffer->wrtn = 0;
    263         buffer->invalidateCommit = false;
    264 
    265         errorWriting = false;
    266     }
    267 
    268     // -------------------------------------------------------------------
    269     // read operations
    270 
    271     /*
    272      * Read a single boolean value.
    273      * Returns false if reading fails.
    274      */
    275     bool readBool() noexcept
    276     {
    277         bool b = false;
    278         return tryRead(&b, sizeof(bool)) ? b : false;
    279     }
    280 
    281     /*
    282      * Read a single 8-bit byte.
    283      * Returns 0 if reading fails.
    284      */
    285     uint8_t readByte() noexcept
    286     {
    287         uint8_t B = 0;
    288         return tryRead(&B, sizeof(uint8_t)) ? B : 0;
    289     }
    290 
    291     /*
    292      * Read a short 16-bit integer.
    293      * Returns 0 if reading fails.
    294      */
    295     int16_t readShort() noexcept
    296     {
    297         int16_t s = 0;
    298         return tryRead(&s, sizeof(int16_t)) ? s : 0;
    299     }
    300 
    301     /*
    302      * Read a short unsigned 16-bit integer.
    303      * Returns 0 if reading fails.
    304      */
    305     uint16_t readUShort() noexcept
    306     {
    307         uint16_t us = 0;
    308         return tryRead(&us, sizeof(uint16_t)) ? us : 0;
    309     }
    310 
    311     /*
    312      * Read a regular 32-bit integer.
    313      * Returns 0 if reading fails.
    314      */
    315     int32_t readInt() noexcept
    316     {
    317         int32_t i = 0;
    318         return tryRead(&i, sizeof(int32_t)) ? i : 0;
    319     }
    320 
    321     /*
    322      * Read an unsigned 32-bit integer.
    323      * Returns 0 if reading fails.
    324      */
    325     uint32_t readUInt() noexcept
    326     {
    327         uint32_t ui = 0;
    328         return tryRead(&ui, sizeof(int32_t)) ? ui : 0;
    329     }
    330 
    331     /*
    332      * Read a long 64-bit integer.
    333      * Returns 0 if reading fails.
    334      */
    335     int64_t readLong() noexcept
    336     {
    337         int64_t l = 0;
    338         return tryRead(&l, sizeof(int64_t)) ? l : 0;
    339     }
    340 
    341     /*
    342      * Read a long unsigned 64-bit integer.
    343      * Returns 0 if reading fails.
    344      */
    345     uint64_t readULong() noexcept
    346     {
    347         uint64_t ul = 0;
    348         return tryRead(&ul, sizeof(int64_t)) ? ul : 0;
    349     }
    350 
    351     /*
    352      * Read a single-precision floating point number.
    353      * Returns 0 if reading fails.
    354      */
    355     float readFloat() noexcept
    356     {
    357         float f = 0.0f;
    358         return tryRead(&f, sizeof(float)) ? f : 0.0f;
    359     }
    360 
    361     /*
    362      * Read a double-precision floating point number.
    363      * Returns 0 if reading fails.
    364      */
    365     double readDouble() noexcept
    366     {
    367         double d = 0.0;
    368         return tryRead(&d, sizeof(double)) ? d : 0.0;
    369     }
    370 
    371     /*!
    372      * Read an arbitrary amount of data, specified by @a size.
    373      * data pointer must be non-null, and size > 0.
    374      *
    375      * Returns true if reading succeeds.
    376      * In case of failure, @a data pointer is automatically cleared by @a size bytes.
    377      */
    378     bool readCustomData(void* const data, const uint32_t size) noexcept
    379     {
    380         DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, false);
    381         DISTRHO_SAFE_ASSERT_RETURN(size > 0, false);
    382 
    383         if (tryRead(data, size))
    384             return true;
    385 
    386         std::memset(data, 0, size);
    387         return false;
    388     }
    389 
    390     /*!
    391      * Read a custom data type specified by the template typename used,
    392      * with size being automatically deduced by the compiler (through the use of sizeof).
    393      *
    394      * Returns true if reading succeeds.
    395      * In case of failure, @a type value is automatically cleared by its deduced size.
    396      */
    397     template <typename T>
    398     bool readCustomType(T& type) noexcept
    399     {
    400         if (tryRead(&type, sizeof(T)))
    401             return true;
    402 
    403         std::memset(&type, 0, sizeof(T));
    404         return false;
    405     }
    406 
    407     // -------------------------------------------------------------------
    408     // peek operations (returns a value without advancing read position)
    409 
    410     /*
    411      * Peek for an unsigned 32-bit integer.
    412      * Returns 0 if reading fails.
    413      */
    414     uint32_t peekUInt() const noexcept
    415     {
    416         uint32_t ui = 0;
    417         return tryPeek(&ui, sizeof(int32_t)) ? ui : 0;
    418     }
    419 
    420     /*!
    421      * Peek for a custom data type specified by the template typename used,
    422      * with size being automatically deduced by the compiler (through the use of sizeof).
    423      *
    424      * Returns true if peeking succeeds.
    425      * In case of failure, @a type value is automatically cleared by its deduced size.
    426      */
    427     template <typename T>
    428     bool peekCustomType(T& type) const noexcept
    429     {
    430         if (tryPeek(&type, sizeof(T)))
    431             return true;
    432 
    433         std::memset(&type, 0, sizeof(T));
    434         return false;
    435     }
    436 
    437     // -------------------------------------------------------------------
    438     // write operations
    439 
    440     /*
    441      * Write a single boolean value.
    442      */
    443     bool writeBool(const bool value) noexcept
    444     {
    445         return tryWrite(&value, sizeof(bool));
    446     }
    447 
    448     /*
    449      * Write a single 8-bit byte.
    450      */
    451     bool writeByte(const uint8_t value) noexcept
    452     {
    453         return tryWrite(&value, sizeof(uint8_t));
    454     }
    455 
    456     /*
    457      * Write a short 16-bit integer.
    458      */
    459     bool writeShort(const int16_t value) noexcept
    460     {
    461         return tryWrite(&value, sizeof(int16_t));
    462     }
    463 
    464     /*
    465      * Write a short unsigned 16-bit integer.
    466      */
    467     bool writeUShort(const uint16_t value) noexcept
    468     {
    469         return tryWrite(&value, sizeof(uint16_t));
    470     }
    471 
    472     /*
    473      * Write a regular 32-bit integer.
    474      */
    475     bool writeInt(const int32_t value) noexcept
    476     {
    477         return tryWrite(&value, sizeof(int32_t));
    478     }
    479 
    480     /*
    481      * Write an unsigned 32-bit integer.
    482      */
    483     bool writeUInt(const uint32_t value) noexcept
    484     {
    485         return tryWrite(&value, sizeof(uint32_t));
    486     }
    487 
    488     /*
    489      * Write a long 64-bit integer.
    490      */
    491     bool writeLong(const int64_t value) noexcept
    492     {
    493         return tryWrite(&value, sizeof(int64_t));
    494     }
    495 
    496     /*
    497      * Write a long unsigned 64-bit integer.
    498      */
    499     bool writeULong(const uint64_t value) noexcept
    500     {
    501         return tryWrite(&value, sizeof(uint64_t));
    502     }
    503 
    504     /*
    505      * Write a single-precision floating point number.
    506      */
    507     bool writeFloat(const float value) noexcept
    508     {
    509         return tryWrite(&value, sizeof(float));
    510     }
    511 
    512     /*
    513      * Write a double-precision floating point number.
    514      */
    515     bool writeDouble(const double value) noexcept
    516     {
    517         return tryWrite(&value, sizeof(double));
    518     }
    519 
    520     /*!
    521      * Write an arbitrary amount of data, specified by @a size.
    522      * data pointer must be non-null, and size > 0.
    523      */
    524     bool writeCustomData(const void* const data, const uint32_t size) noexcept
    525     {
    526         DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, false);
    527         DISTRHO_SAFE_ASSERT_RETURN(size > 0, false);
    528 
    529         return tryWrite(data, size);
    530     }
    531 
    532     /*!
    533      * Write a custom data type specified by the template typename used,
    534      * with size being automatically deduced by the compiler (through the use of sizeof).
    535      */
    536     template <typename T>
    537     bool writeCustomType(const T& type) noexcept
    538     {
    539         return tryWrite(&type, sizeof(T));
    540     }
    541 
    542     // -------------------------------------------------------------------
    543 
    544     /*!
    545      * Commit all previous write operations to the ringbuffer.
    546      * If a write operation has previously failed, this will reset/invalidate the previous write attempts.
    547      */
    548     bool commitWrite() noexcept
    549     {
    550         DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr, false);
    551 
    552         if (buffer->invalidateCommit)
    553         {
    554             buffer->wrtn = buffer->head;
    555             buffer->invalidateCommit = false;
    556             return false;
    557         }
    558 
    559         // nothing to commit?
    560         DISTRHO_SAFE_ASSERT_RETURN(buffer->head != buffer->wrtn, false);
    561 
    562         // all ok
    563         buffer->head = buffer->wrtn;
    564         errorWriting = false;
    565         return true;
    566     }
    567 
    568     // -------------------------------------------------------------------
    569 
    570     /*
    571      * Tie this ring buffer control to a ring buffer struct, optionally clearing its data.
    572      */
    573     void setRingBuffer(BufferStruct* const ringBuf, const bool clearRingBufferData) noexcept
    574     {
    575         DISTRHO_SAFE_ASSERT_RETURN(buffer != ringBuf,);
    576 
    577         buffer = ringBuf;
    578 
    579         if (clearRingBufferData && ringBuf != nullptr)
    580             clearData();
    581     }
    582 
    583     // -------------------------------------------------------------------
    584 
    585 protected:
    586     /** @internal try reading from the buffer, can fail. */
    587     bool tryRead(void* const buf, const uint32_t size) noexcept
    588     {
    589         DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr, false);
    590        #if defined(__clang__)
    591         #pragma clang diagnostic push
    592         #pragma clang diagnostic ignored "-Wtautological-pointer-compare"
    593        #endif
    594         DISTRHO_SAFE_ASSERT_RETURN(buffer->buf != nullptr, false);
    595        #if defined(__clang__)
    596         #pragma clang diagnostic pop
    597        #endif
    598         DISTRHO_SAFE_ASSERT_RETURN(buf != nullptr, false);
    599         DISTRHO_SAFE_ASSERT_RETURN(size > 0, false);
    600         DISTRHO_SAFE_ASSERT_RETURN(size < buffer->size, false);
    601 
    602         // empty
    603         if (buffer->head == buffer->tail)
    604             return false;
    605 
    606         uint8_t* const bytebuf = static_cast<uint8_t*>(buf);
    607 
    608         const uint32_t head = buffer->head;
    609         const uint32_t tail = buffer->tail;
    610         const uint32_t wrap = head > tail ? 0 : buffer->size;
    611 
    612         if (size > wrap + head - tail)
    613         {
    614             if (! errorReading)
    615             {
    616                 errorReading = true;
    617                 d_stderr2("RingBuffer::tryRead(%p, %lu): failed, not enough space", buf, (ulong)size);
    618             }
    619             return false;
    620         }
    621 
    622         uint32_t readto = tail + size;
    623 
    624         if (readto > buffer->size)
    625         {
    626             readto -= buffer->size;
    627 
    628             if (size == 1)
    629             {
    630                 std::memcpy(bytebuf, buffer->buf + tail, 1);
    631             }
    632             else
    633             {
    634                 const uint32_t firstpart = buffer->size - tail;
    635                 std::memcpy(bytebuf, buffer->buf + tail, firstpart);
    636                 std::memcpy(bytebuf + firstpart, buffer->buf, readto);
    637             }
    638         }
    639         else
    640         {
    641             std::memcpy(bytebuf, buffer->buf + tail, size);
    642 
    643             if (readto == buffer->size)
    644                 readto = 0;
    645         }
    646 
    647         buffer->tail = readto;
    648         errorReading = false;
    649         return true;
    650     }
    651 
    652     /** @internal try reading from the buffer, can fail. */
    653     bool tryPeek(void* const buf, const uint32_t size) const noexcept
    654     {
    655         DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr, false);
    656        #if defined(__clang__)
    657         #pragma clang diagnostic push
    658         #pragma clang diagnostic ignored "-Wtautological-pointer-compare"
    659        #endif
    660         DISTRHO_SAFE_ASSERT_RETURN(buffer->buf != nullptr, false);
    661        #if defined(__clang__)
    662         #pragma clang diagnostic pop
    663        #endif
    664         DISTRHO_SAFE_ASSERT_RETURN(buf != nullptr, false);
    665         DISTRHO_SAFE_ASSERT_RETURN(size > 0, false);
    666         DISTRHO_SAFE_ASSERT_RETURN(size < buffer->size, false);
    667 
    668         // empty
    669         if (buffer->head == buffer->tail)
    670             return false;
    671 
    672         uint8_t* const bytebuf = static_cast<uint8_t*>(buf);
    673 
    674         const uint32_t head = buffer->head;
    675         const uint32_t tail = buffer->tail;
    676         const uint32_t wrap = head > tail ? 0 : buffer->size;
    677 
    678         if (size > wrap + head - tail)
    679             return false;
    680 
    681         uint32_t readto = tail + size;
    682 
    683         if (readto > buffer->size)
    684         {
    685             readto -= buffer->size;
    686 
    687             if (size == 1)
    688             {
    689                 std::memcpy(bytebuf, buffer->buf + tail, 1);
    690             }
    691             else
    692             {
    693                 const uint32_t firstpart = buffer->size - tail;
    694                 std::memcpy(bytebuf, buffer->buf + tail, firstpart);
    695                 std::memcpy(bytebuf + firstpart, buffer->buf, readto);
    696             }
    697         }
    698         else
    699         {
    700             std::memcpy(bytebuf, buffer->buf + tail, size);
    701 
    702             if (readto == buffer->size)
    703                 readto = 0;
    704         }
    705 
    706         return true;
    707     }
    708 
    709     /** @internal try writing to the buffer, can fail. */
    710     bool tryWrite(const void* const buf, const uint32_t size) noexcept
    711     {
    712         DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr, false);
    713         DISTRHO_SAFE_ASSERT_RETURN(buf != nullptr, false);
    714         DISTRHO_SAFE_ASSERT_RETURN(size > 0, false);
    715         DISTRHO_SAFE_ASSERT_UINT2_RETURN(size < buffer->size, size, buffer->size, false);
    716 
    717         const uint8_t* const bytebuf = static_cast<const uint8_t*>(buf);
    718 
    719         const uint32_t tail = buffer->tail;
    720         const uint32_t wrtn = buffer->wrtn;
    721         const uint32_t wrap = tail > wrtn ? 0 : buffer->size;
    722 
    723         if (size >= wrap + tail - wrtn)
    724         {
    725             if (! errorWriting)
    726             {
    727                 errorWriting = true;
    728                 d_stderr2("RingBuffer::tryWrite(%p, %lu): failed, not enough space", buf, (ulong)size);
    729             }
    730             buffer->invalidateCommit = true;
    731             return false;
    732         }
    733 
    734         uint32_t writeto = wrtn + size;
    735 
    736         if (writeto > buffer->size)
    737         {
    738             writeto -= buffer->size;
    739 
    740             if (size == 1)
    741             {
    742                 std::memcpy(buffer->buf, bytebuf, 1);
    743             }
    744             else
    745             {
    746                 const uint32_t firstpart = buffer->size - wrtn;
    747                 std::memcpy(buffer->buf + wrtn, bytebuf, firstpart);
    748                 std::memcpy(buffer->buf, bytebuf + firstpart, writeto);
    749             }
    750         }
    751         else
    752         {
    753             std::memcpy(buffer->buf + wrtn, bytebuf, size);
    754 
    755             if (writeto == buffer->size)
    756                 writeto = 0;
    757         }
    758 
    759         buffer->wrtn = writeto;
    760         return true;
    761     }
    762 
    763 private:
    764     /** Buffer struct pointer. */
    765     BufferStruct* buffer;
    766 
    767     /** Whether read errors have been printed to terminal. */
    768     bool errorReading;
    769 
    770     /** Whether write errors have been printed to terminal. */
    771     bool errorWriting;
    772 
    773     DISTRHO_PREVENT_VIRTUAL_HEAP_ALLOCATION
    774     DISTRHO_DECLARE_NON_COPYABLE(RingBufferControl)
    775 };
    776 
    777 template <class BufferStruct>
    778 inline bool RingBufferControl<BufferStruct>::isDataAvailableForReading() const noexcept
    779 {
    780     return (buffer != nullptr && buffer->head != buffer->tail);
    781 }
    782 
    783 template <>
    784 inline bool RingBufferControl<HeapBuffer>::isDataAvailableForReading() const noexcept
    785 {
    786     return (buffer != nullptr && buffer->buf != nullptr && buffer->head != buffer->tail);
    787 }
    788 
    789 // -----------------------------------------------------------------------
    790 // RingBuffer using heap space
    791 
    792 /**
    793    RingBufferControl with a heap buffer.
    794    This is a convenience class that provides a method for creating and destroying the heap data.
    795    Requires the use of createBuffer(uint32_t) to make the ring buffer usable.
    796 */
    797 class HeapRingBuffer : public RingBufferControl<HeapBuffer>
    798 {
    799 public:
    800     /** Constructor. */
    801     HeapRingBuffer() noexcept
    802         : heapBuffer(HeapBuffer_INIT)
    803     {
    804 #ifndef DISTRHO_PROPER_CPP11_SUPPORT
    805         std::memset(&heapBuffer, 0, sizeof(heapBuffer));
    806 #endif
    807     }
    808 
    809     /** Destructor. */
    810     ~HeapRingBuffer() noexcept override
    811     {
    812         if (heapBuffer.buf == nullptr)
    813             return;
    814 
    815         delete[] heapBuffer.buf;
    816         heapBuffer.buf = nullptr;
    817     }
    818 
    819     /** Create a buffer of the specified size. */
    820     bool createBuffer(const uint32_t size) noexcept
    821     {
    822         DISTRHO_SAFE_ASSERT_RETURN(heapBuffer.buf == nullptr, false);
    823         DISTRHO_SAFE_ASSERT_RETURN(size > 0,  false);
    824 
    825         const uint32_t p2size = d_nextPowerOf2(size);
    826 
    827         try {
    828             heapBuffer.buf = new uint8_t[p2size];
    829         } DISTRHO_SAFE_EXCEPTION_RETURN("HeapRingBuffer::createBuffer", false);
    830 
    831         heapBuffer.size = p2size;
    832         setRingBuffer(&heapBuffer, true);
    833         return true;
    834     }
    835 
    836     /** Delete the previously allocated buffer. */
    837     void deleteBuffer() noexcept
    838     {
    839         DISTRHO_SAFE_ASSERT_RETURN(heapBuffer.buf != nullptr,);
    840 
    841         setRingBuffer(nullptr, false);
    842 
    843         delete[] heapBuffer.buf;
    844         heapBuffer.buf  = nullptr;
    845         heapBuffer.size = 0;
    846     }
    847 
    848     void copyFromAndClearOther(HeapRingBuffer& other)
    849     {
    850         DISTRHO_SAFE_ASSERT_RETURN(other.heapBuffer.size == heapBuffer.size,);
    851 
    852         std::memcpy(&heapBuffer, &other.heapBuffer, sizeof(HeapBuffer) - sizeof(uint8_t*));
    853         std::memcpy(heapBuffer.buf, other.heapBuffer.buf, sizeof(uint8_t) * heapBuffer.size);
    854         other.clearData();
    855     }
    856 
    857 private:
    858     /** The heap buffer used for this class. */
    859     HeapBuffer heapBuffer;
    860 
    861     DISTRHO_PREVENT_VIRTUAL_HEAP_ALLOCATION
    862     DISTRHO_DECLARE_NON_COPYABLE(HeapRingBuffer)
    863 };
    864 
    865 // -----------------------------------------------------------------------
    866 // RingBuffer using small stack space
    867 
    868 /**
    869    RingBufferControl with an included small stack buffer.
    870    No setup is necessary, this class is usable as-is.
    871 */
    872 class SmallStackRingBuffer : public RingBufferControl<SmallStackBuffer>
    873 {
    874 public:
    875     /** Constructor. */
    876     SmallStackRingBuffer() noexcept
    877         : stackBuffer(StackBuffer_INIT)
    878     {
    879 #ifndef DISTRHO_PROPER_CPP11_SUPPORT
    880         std::memset(&stackBuffer, 0, sizeof(stackBuffer));
    881 #endif
    882         setRingBuffer(&stackBuffer, true);
    883     }
    884 
    885 private:
    886     /** The small stack buffer used for this class. */
    887     SmallStackBuffer stackBuffer;
    888 
    889     DISTRHO_PREVENT_VIRTUAL_HEAP_ALLOCATION
    890     DISTRHO_DECLARE_NON_COPYABLE(SmallStackRingBuffer)
    891 };
    892 
    893 // -----------------------------------------------------------------------
    894 
    895 END_NAMESPACE_DISTRHO
    896 
    897 #endif // DISTRHO_RING_BUFFER_HPP_INCLUDED