commit b870872b0df225fc2a894f2d7836a5fb54c2f8b9
parent a764e196cf876924a8447c71bc3f3dad83224e7e
Author: falkTX <falktx@falktx.com>
Date: Mon, 24 May 2021 17:37:11 +0100
Add ring buffer class
Signed-off-by: falkTX <falktx@falktx.com>
Diffstat:
2 files changed, 769 insertions(+), 0 deletions(-)
diff --git a/distrho/extra/RingBuffer.hpp b/distrho/extra/RingBuffer.hpp
@@ -0,0 +1,758 @@
+/*
+ * DISTRHO Plugin Framework (DPF)
+ * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any purpose with
+ * or without fee is hereby granted, provided that the above copyright notice and this
+ * permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
+ * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef DISTRHO_RING_BUFFER_HPP_INCLUDED
+#define DISTRHO_RING_BUFFER_HPP_INCLUDED
+
+#include "../DistrhoUtils.hpp"
+
+START_NAMESPACE_DISTRHO
+
+// -----------------------------------------------------------------------
+// Buffer structs
+
+/**
+ Base structure for all RingBuffer containers.
+ This struct details the data model used in DPF's RingBuffer class.
+
+ DPF RingBuffer uses a struct just like this one to store positions, buffer data, size, etc.
+ The RingBuffer itself takes ownership of this struct and uses it to store any needed data.
+ This allows to dynamically change the way its ring buffer is allocated, simply by changing the template type.
+ For example, `RingBufferControl<HeapBuffer>` will create a ring buffer with heap memory, which can be of any size.
+ In the same vein, `RingBufferControl<SmallStackBuffer>` will create a ring buffer with stack memory,
+ directly tied to the RingBufferControl it belongs to.
+
+ The main idea behind this model is to allow RingBufferControl over memory created elsewhere,
+ for example shared memory area.
+ One can create/place the Buffer struct in shared memory, and point RingBufferControl to it,
+ thus avoiding the pitfalls of sharing access to a non trivially-copyable/POD C++ class.
+
+ Unlike other ring buffers, an extra variable is used to track pending writes.
+ This is so we can write a few bytes at a time and later mark the whole operation as complete,
+ thus avoiding the issue of reading data too early from the other side.
+ For example, write the size of some data first, and then the actual data.
+ The reading side will only see data available once size + data is completely written and "committed".
+ */
+struct HeapBuffer {
+ /**
+ Size of the buffer, allocated in @a buf.
+ If the size is fixed (stack buffer), this variable can be static.
+ */
+ uint32_t size;
+
+ /**
+ Current writing position, headmost position of the buffer.
+ Increments when writing.
+ */
+ uint32_t head;
+
+ /**
+ Current reading position, last used position of the buffer.
+ Increments when reading.
+ head == tail means empty buffer.
+ */
+ uint32_t tail;
+
+ /**
+ 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.
+ */
+ uint32_t wrtn;
+
+ /**
+ Boolean used to check if a write operation failed.
+ This ensures we don't get incomplete writes.
+ */
+ bool invalidateCommit;
+
+ /**
+ Pointer to buffer data.
+ This can be either stack or heap data, depending on the usecase.
+ */
+ uint8_t* buf;
+};
+
+/**
+ RingBufferControl compatible struct with a relatively small stack size (4k bytes).
+ @see HeapBuffer
+*/
+struct SmallStackBuffer {
+ static const uint32_t size = 4096;
+ uint32_t head, tail, wrtn;
+ bool invalidateCommit;
+ uint8_t buf[size];
+};
+
+/**
+ RingBufferControl compatible struct with a relatively big stack size (16k bytes).
+ @see HeapBuffer
+*/
+struct BigStackBuffer {
+ static const uint32_t size = 16384;
+ uint32_t head, tail, wrtn;
+ bool invalidateCommit;
+ uint8_t buf[size];
+};
+
+/**
+ RingBufferControl compatible struct with a huge stack size (64k bytes).
+ @see HeapBuffer
+*/
+struct HugeStackBuffer {
+ static const uint32_t size = 65536;
+ uint32_t head, tail, wrtn;
+ bool invalidateCommit;
+ uint8_t buf[size];
+};
+
+// -----------------------------------------------------------------------
+// RingBufferControl templated class
+
+/**
+ DPF built-in RingBuffer class.
+ RingBufferControl takes one buffer struct to take control over, and operates over it.
+
+ This is meant for single-writer, single-reader type of control.
+ Writing and reading is wait and lock-free.
+
+ Typically usage involves:
+ ```
+ // definition
+ RingBufferControl<HeapBuffer> myHeapBuffer; // or HeapRingBuffer class directly
+
+ // construction, only needed for heap buffers
+ myHeapBuffer.createBuffer(8192);
+
+ // writing data
+ myHeapBuffer.writeUInt(size);
+ myHeapBuffer.writeCustomData(someOtherData, size);
+ myHeapBuffer.commitWrite();
+
+ // reading data
+ if (myHeapBuffer.isDataAvailableForReading())
+ {
+ uint32_t size;
+ if (myHeapBuffer.readUInt(size) && readCustomData(&anotherData, size))
+ {
+ // do something with "anotherData"
+ }
+ }
+ ```
+
+ @see HeapBuffer
+ */
+template <class BufferStruct>
+class RingBufferControl
+{
+public:
+ /*
+ * Constructor for unitialized ring buffer.
+ * A call to setRingBuffer is required to tied this control to a ring buffer struct;
+ *
+ */
+ RingBufferControl() noexcept
+ : buffer(nullptr),
+ errorReading(false),
+ errorWriting(false) {}
+
+ /*
+ * Destructor.
+ */
+ virtual ~RingBufferControl() noexcept {}
+
+ // -------------------------------------------------------------------
+ // check operations
+
+ /*
+ * Check if there is any data available for reading, regardless of size.
+ */
+ bool isDataAvailableForReading() const noexcept;
+
+ /*
+ * Check if ring buffer is empty (that is, there is nothing to read).
+ */
+ bool isEmpty() const noexcept
+ {
+ DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr, false);
+
+ return (buffer->buf == nullptr || buffer->head == buffer->tail);
+ }
+
+ /*
+ * Get the size of the data available to read.
+ */
+ uint32_t getAvailableDataSize() const noexcept
+ {
+ DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr, 0);
+
+ const uint32_t wrap((buffer->tail > buffer->wrtn) ? 0 : buffer->size);
+
+ return wrap + buffer->tail - buffer->wrtn;
+ }
+
+ // -------------------------------------------------------------------
+ // clear/reset operations
+
+ /*
+ * Clear the entire ring buffer data, marking the buffer as empty.
+ * Requires a buffer struct tied to this class.
+ */
+ void clearData() noexcept
+ {
+ DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr,);
+
+ buffer->head = 0;
+ buffer->tail = 0;
+ buffer->wrtn = 0;
+ buffer->invalidateCommit = false;
+
+ std::memset(buffer->buf, 0, buffer->size);
+ }
+
+ // -------------------------------------------------------------------
+ // read operations
+
+ /*
+ * Read a single boolean value.
+ * Returns false if reading fails.
+ */
+ bool readBool() noexcept
+ {
+ bool b = false;
+ return tryRead(&b, sizeof(bool)) ? b : false;
+ }
+
+ /*
+ * Read a single 8-bit byte.
+ * Returns 0 if reading fails.
+ */
+ uint8_t readByte() noexcept
+ {
+ uint8_t B = 0;
+ return tryRead(&B, sizeof(uint8_t)) ? B : 0;
+ }
+
+ /*
+ * Read a short 16-bit integer.
+ * Returns 0 if reading fails.
+ */
+ int16_t readShort() noexcept
+ {
+ int16_t s = 0;
+ return tryRead(&s, sizeof(int16_t)) ? s : 0;
+ }
+
+ /*
+ * Read a short unsigned 16-bit integer.
+ * Returns 0 if reading fails.
+ */
+ uint16_t readUShort() noexcept
+ {
+ uint16_t us = 0;
+ return tryRead(&us, sizeof(uint16_t)) ? us : 0;
+ }
+
+ /*
+ * Read a regular 32-bit integer.
+ * Returns 0 if reading fails.
+ */
+ int32_t readInt() noexcept
+ {
+ int32_t i = 0;
+ return tryRead(&i, sizeof(int32_t)) ? i : 0;
+ }
+
+ /*
+ * Read an unsigned 32-bit integer.
+ * Returns 0 if reading fails.
+ */
+ uint32_t readUInt() noexcept
+ {
+ uint32_t ui = 0;
+ return tryRead(&ui, sizeof(int32_t)) ? ui : 0;
+ }
+
+ /*
+ * Read a long 64-bit integer.
+ * Returns 0 if reading fails.
+ */
+ int64_t readLong() noexcept
+ {
+ int64_t l = 0;
+ return tryRead(&l, sizeof(int64_t)) ? l : 0;
+ }
+
+ /*
+ * Read a long unsigned 64-bit integer.
+ * Returns 0 if reading fails.
+ */
+ uint64_t readULong() noexcept
+ {
+ uint64_t ul = 0;
+ return tryRead(&ul, sizeof(int64_t)) ? ul : 0;
+ }
+
+ /*
+ * Read a single-precision floating point number.
+ * Returns 0 if reading fails.
+ */
+ float readFloat() noexcept
+ {
+ float f = 0.0f;
+ return tryRead(&f, sizeof(float)) ? f : 0.0f;
+ }
+
+ /*
+ * Read a double-precision floating point number.
+ * Returns 0 if reading fails.
+ */
+ double readDouble() noexcept
+ {
+ double d = 0.0;
+ return tryRead(&d, sizeof(double)) ? d : 0.0;
+ }
+
+ /*!
+ * Read an arbitrary amount of data, specified by @a size.
+ * data pointer must be non-null, and size > 0.
+ *
+ * Returns true if reading succeeds.
+ * In case of failure, @a data pointer is automatically cleared by @a size bytes.
+ */
+ bool readCustomData(void* const data, const uint32_t size) noexcept
+ {
+ DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, false);
+ DISTRHO_SAFE_ASSERT_RETURN(size > 0, false);
+
+ if (tryRead(data, size))
+ return true;
+
+ std::memset(data, 0, size);
+ return false;
+ }
+
+ /*!
+ * Read a custom data type specified by the template typename used,
+ * with size being automatically deduced by the compiler (through the use of sizeof).
+ *
+ * Returns true if reading succeeds.
+ * In case of failure, @a type value is automatically cleared by its deduced size.
+ */
+ template <typename T>
+ bool readCustomType(T& type) noexcept
+ {
+ if (tryRead(&type, sizeof(T)))
+ return true;
+
+ std::memset(&type, 0, sizeof(T));
+ return false;
+ }
+
+ // -------------------------------------------------------------------
+ // write operations
+
+ /*
+ * Write a single boolean value.
+ */
+ bool writeBool(const bool value) noexcept
+ {
+ return tryWrite(&value, sizeof(bool));
+ }
+
+ /*
+ * Write a single 8-bit byte.
+ */
+ bool writeByte(const uint8_t value) noexcept
+ {
+ return tryWrite(&value, sizeof(uint8_t));
+ }
+
+ /*
+ * Write a short 16-bit integer.
+ */
+ bool writeShort(const int16_t value) noexcept
+ {
+ return tryWrite(&value, sizeof(int16_t));
+ }
+
+ /*
+ * Write a short unsigned 16-bit integer.
+ */
+ bool writeUShort(const uint16_t value) noexcept
+ {
+ return tryWrite(&value, sizeof(uint16_t));
+ }
+
+ /*
+ * Write a regular 32-bit integer.
+ */
+ bool writeInt(const int32_t value) noexcept
+ {
+ return tryWrite(&value, sizeof(int32_t));
+ }
+
+ /*
+ * Write an unsigned 32-bit integer.
+ */
+ bool writeUInt(const uint32_t value) noexcept
+ {
+ return tryWrite(&value, sizeof(uint32_t));
+ }
+
+ /*
+ * Write a long 64-bit integer.
+ */
+ bool writeLong(const int64_t value) noexcept
+ {
+ return tryWrite(&value, sizeof(int64_t));
+ }
+
+ /*
+ * Write a long unsigned 64-bit integer.
+ */
+ bool writeULong(const uint64_t value) noexcept
+ {
+ return tryWrite(&value, sizeof(uint64_t));
+ }
+
+ /*
+ * Write a single-precision floating point number.
+ */
+ bool writeFloat(const float value) noexcept
+ {
+ return tryWrite(&value, sizeof(float));
+ }
+
+ /*
+ * Write a double-precision floating point number.
+ */
+ bool writeDouble(const double value) noexcept
+ {
+ return tryWrite(&value, sizeof(double));
+ }
+
+ /*!
+ * Write an arbitrary amount of data, specified by @a size.
+ * data pointer must be non-null, and size > 0.
+ */
+ bool writeCustomData(const void* const data, const uint32_t size) noexcept
+ {
+ DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, false);
+ DISTRHO_SAFE_ASSERT_RETURN(size > 0, false);
+
+ return tryWrite(data, size);
+ }
+
+ /*!
+ * Write a custom data type specified by the template typename used,
+ * with size being automatically deduced by the compiler (through the use of sizeof).
+ */
+ template <typename T>
+ bool writeCustomType(const T& type) noexcept
+ {
+ return tryWrite(&type, sizeof(T));
+ }
+
+ // -------------------------------------------------------------------
+
+ /*!
+ * Commit all previous write operations to the ringbuffer.
+ * If a write operation has previously failed, this will reset/invalidate the previous write attempts.
+ */
+ bool commitWrite() noexcept
+ {
+ DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr, false);
+
+ if (buffer->invalidateCommit)
+ {
+ buffer->wrtn = buffer->head;
+ buffer->invalidateCommit = false;
+ return false;
+ }
+
+ // nothing to commit?
+ DISTRHO_SAFE_ASSERT_RETURN(buffer->head != buffer->wrtn, false);
+
+ // all ok
+ buffer->head = buffer->wrtn;
+ errorWriting = false;
+ return true;
+ }
+
+ // -------------------------------------------------------------------
+
+protected:
+ /*
+ * Tie this ring buffer control to a ring buffer struct, optionally clearing its data.
+ */
+ void setRingBuffer(BufferStruct* const ringBuf, const bool clearRingBufferData) noexcept
+ {
+ DISTRHO_SAFE_ASSERT_RETURN(buffer != ringBuf,);
+
+ buffer = ringBuf;
+
+ if (clearRingBufferData && ringBuf != nullptr)
+ clearData();
+ }
+
+ // -------------------------------------------------------------------
+
+ /** @internal try reading from the buffer, can fail. */
+ bool tryRead(void* const buf, const uint32_t size) noexcept
+ {
+ DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr, false);
+ #if defined(__clang__)
+ # pragma clang diagnostic push
+ # pragma clang diagnostic ignored "-Wtautological-pointer-compare"
+ #endif
+ DISTRHO_SAFE_ASSERT_RETURN(buffer->buf != nullptr, false);
+ #if defined(__clang__)
+ # pragma clang diagnostic pop
+ #endif
+ DISTRHO_SAFE_ASSERT_RETURN(buf != nullptr, false);
+ DISTRHO_SAFE_ASSERT_RETURN(size > 0, false);
+ DISTRHO_SAFE_ASSERT_RETURN(size < buffer->size, false);
+
+ // empty
+ if (buffer->head == buffer->tail)
+ return false;
+
+ uint8_t* const bytebuf(static_cast<uint8_t*>(buf));
+
+ const uint32_t head(buffer->head);
+ const uint32_t tail(buffer->tail);
+ const uint32_t wrap((head > tail) ? 0 : buffer->size);
+
+ if (size > wrap + head - tail)
+ {
+ if (! errorReading)
+ {
+ errorReading = true;
+ d_stderr2("RingBuffer::tryRead(%p, %lu): failed, not enough space", buf, (ulong)size);
+ }
+ return false;
+ }
+
+ uint32_t readto(tail + size);
+
+ if (readto > buffer->size)
+ {
+ readto -= buffer->size;
+
+ if (size == 1)
+ {
+ std::memcpy(bytebuf, buffer->buf + tail, 1);
+ }
+ else
+ {
+ const uint32_t firstpart(buffer->size - tail);
+ std::memcpy(bytebuf, buffer->buf + tail, firstpart);
+ std::memcpy(bytebuf + firstpart, buffer->buf, readto);
+ }
+ }
+ else
+ {
+ std::memcpy(bytebuf, buffer->buf + tail, size);
+
+ if (readto == buffer->size)
+ readto = 0;
+ }
+
+ buffer->tail = readto;
+ errorReading = false;
+ return true;
+ }
+
+ /** @internal try writing to the buffer, can fail. */
+ bool tryWrite(const void* const buf, const uint32_t size) noexcept
+ {
+ DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr, false);
+ DISTRHO_SAFE_ASSERT_RETURN(buf != nullptr, false);
+ DISTRHO_SAFE_ASSERT_RETURN(size > 0, false);
+ CARLA_SAFE_ASSERT_UINT2_RETURN(size < buffer->size, size, buffer->size, false);
+
+ const uint8_t* const bytebuf(static_cast<const uint8_t*>(buf));
+
+ const uint32_t tail(buffer->tail);
+ const uint32_t wrtn(buffer->wrtn);
+ const uint32_t wrap((tail > wrtn) ? 0 : buffer->size);
+
+ if (size >= wrap + tail - wrtn)
+ {
+ if (! errorWriting)
+ {
+ errorWriting = true;
+ d_stderr2("RingBuffer::tryWrite(%p, %lu): failed, not enough space", buf, (ulong)size);
+ }
+ buffer->invalidateCommit = true;
+ return false;
+ }
+
+ uint32_t writeto(wrtn + size);
+
+ if (writeto > buffer->size)
+ {
+ writeto -= buffer->size;
+
+ if (size == 1)
+ {
+ std::memcpy(buffer->buf, bytebuf, 1);
+ }
+ else
+ {
+ const uint32_t firstpart(buffer->size - wrtn);
+ std::memcpy(buffer->buf + wrtn, bytebuf, firstpart);
+ std::memcpy(buffer->buf, bytebuf + firstpart, writeto);
+ }
+ }
+ else
+ {
+ std::memcpy(buffer->buf + wrtn, bytebuf, size);
+
+ if (writeto == buffer->size)
+ writeto = 0;
+ }
+
+ buffer->wrtn = writeto;
+ return true;
+ }
+
+private:
+ /** Buffer struct pointer. */
+ BufferStruct* buffer;
+
+ /** Whether read errors have been printed to terminal. */
+ bool errorReading;
+
+ /** Whether write errors have been printed to terminal. */
+ bool errorWriting;
+
+ DISTRHO_PREVENT_VIRTUAL_HEAP_ALLOCATION
+ DISTRHO_DECLARE_NON_COPYABLE(RingBufferControl)
+};
+
+template <class BufferStruct>
+inline bool RingBufferControl<BufferStruct>::isDataAvailableForReading() const noexcept
+{
+ return (buffer != nullptr && buffer->head != buffer->tail);
+}
+
+template <>
+inline bool RingBufferControl<HeapBuffer>::isDataAvailableForReading() const noexcept
+{
+ return (buffer != nullptr && buffer->buf != nullptr && buffer->head != buffer->tail);
+}
+
+// -----------------------------------------------------------------------
+// RingBuffer using heap space
+
+/**
+ RingBufferControl with a heap buffer.
+ This is a convenience class that provides a method for creating and destroying the heap data.
+ Requires the use of createBuffer(uint32_t) to make the ring buffer usable.
+*/
+class HeapRingBuffer : public RingBufferControl<HeapBuffer>
+{
+public:
+ /** Constructor. */
+ HeapRingBuffer() noexcept
+ : heapBuffer({0, 0, 0, 0, false, nullptr})
+ {
+#ifndef DISTRHO_PROPER_CPP11_SUPPORT
+ std::memset(&heapBuffer, 0, sizeof(heapBuffer));
+#endif
+ }
+
+ /** Destructor. */
+ ~HeapRingBuffer() noexcept override
+ {
+ if (heapBuffer.buf == nullptr)
+ return;
+
+ delete[] heapBuffer.buf;
+ heapBuffer.buf = nullptr;
+ }
+
+ /** Create a buffer of the specified size. */
+ bool createBuffer(const uint32_t size) noexcept
+ {
+ DISTRHO_SAFE_ASSERT_RETURN(heapBuffer.buf == nullptr, false);
+ DISTRHO_SAFE_ASSERT_RETURN(size > 0, false);
+
+ const uint32_t p2size = d_nextPowerOf2(size);
+
+ try {
+ heapBuffer.buf = new uint8_t[p2size];
+ } DISTRHO_SAFE_EXCEPTION_RETURN("HeapRingBuffer::createBuffer", false);
+
+ heapBuffer.size = p2size;
+ setRingBuffer(&heapBuffer, true);
+ }
+
+ /** Delete the previously allocated buffer. */
+ void deleteBuffer() noexcept
+ {
+ DISTRHO_SAFE_ASSERT_RETURN(heapBuffer.buf != nullptr,);
+
+ setRingBuffer(nullptr, false);
+
+ delete[] heapBuffer.buf;
+ heapBuffer.buf = nullptr;
+ heapBuffer.size = 0;
+ }
+
+private:
+ /** The heap buffer used for this class. */
+ HeapBuffer heapBuffer;
+
+ DISTRHO_PREVENT_VIRTUAL_HEAP_ALLOCATION
+ DISTRHO_DECLARE_NON_COPYABLE(HeapRingBuffer)
+};
+
+// -----------------------------------------------------------------------
+// RingBuffer using small stack space
+
+/**
+ RingBufferControl with an included small stack buffer.
+ No setup is necessary, this class is usable as-is.
+*/
+class SmallStackRingBuffer : public RingBufferControl<SmallStackBuffer>
+{
+public:
+ /** Constructor. */
+ SmallStackRingBuffer() noexcept
+ : stackBuffer({0, 0, 0, false, {0}})
+ {
+#ifndef DISTRHO_PROPER_CPP11_SUPPORT
+ std::memset(&stackBuffer, 0, sizeof(stackBuffer));
+#endif
+ setRingBuffer(&stackBuffer, true);
+ }
+
+private:
+ /** The small stack buffer used for this class. */
+ SmallStackBuffer stackBuffer;
+
+ DISTRHO_PREVENT_VIRTUAL_HEAP_ALLOCATION
+ DISTRHO_DECLARE_NON_COPYABLE(SmallStackRingBuffer)
+};
+
+// -----------------------------------------------------------------------
+
+END_NAMESPACE_DISTRHO
+
+#endif // DISTRHO_RING_BUFFER_HPP_INCLUDED
diff --git a/distrho/src/DistrhoDefines.h b/distrho/src/DistrhoDefines.h
@@ -160,6 +160,17 @@ private: \
static void operator delete(void*);
#endif
+/* Define DISTRHO_PREVENT_VIRTUAL_HEAP_ALLOCATION */
+#ifdef DISTRHO_PROPER_CPP11_SUPPORT
+# define DISTRHO_PREVENT_VIRTUAL_HEAP_ALLOCATION \
+private: \
+ static void* operator new(std::size_t) = delete;
+#else
+# define DISTRHO_PREVENT_VIRTUAL_HEAP_ALLOCATION \
+private: \
+ static void* operator new(std::size_t);
+#endif
+
/* Define namespace */
#ifndef DISTRHO_NAMESPACE
# define DISTRHO_NAMESPACE DISTRHO