comparison DPF-Prymula-audioplugins/dpf/distrho/extra/RingBuffer.hpp @ 3:84e66ea83026

DPF-Prymula-audioplugins-0.231015-2
author prymula <prymula76@outlook.com>
date Mon, 16 Oct 2023 21:53:34 +0200
parents
children
comparison
equal deleted inserted replaced
2:cf2cb71d31dd 3:84e66ea83026
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 size of the data available to read.
205 */
206 uint32_t getReadableDataSize() const noexcept
207 {
208 DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr, 0);
209
210 const uint32_t wrap = buffer->head > buffer->tail ? 0 : buffer->size;
211
212 return wrap + buffer->head - buffer->tail;
213 }
214
215 /*
216 * Get the size of the data available to write.
217 */
218 uint32_t getWritableDataSize() const noexcept
219 {
220 DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr, 0);
221
222 const uint32_t wrap = (buffer->tail > buffer->wrtn) ? 0 : buffer->size;
223
224 return wrap + buffer->tail - buffer->wrtn;
225 }
226
227 // -------------------------------------------------------------------
228 // clear/reset operations
229
230 /*
231 * Clear the entire ring buffer data, marking the buffer as empty.
232 * Requires a buffer struct tied to this class.
233 */
234 void clearData() noexcept
235 {
236 DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr,);
237
238 buffer->head = 0;
239 buffer->tail = 0;
240 buffer->wrtn = 0;
241 buffer->invalidateCommit = false;
242
243 std::memset(buffer->buf, 0, buffer->size);
244 }
245
246 // -------------------------------------------------------------------
247 // read operations
248
249 /*
250 * Read a single boolean value.
251 * Returns false if reading fails.
252 */
253 bool readBool() noexcept
254 {
255 bool b = false;
256 return tryRead(&b, sizeof(bool)) ? b : false;
257 }
258
259 /*
260 * Read a single 8-bit byte.
261 * Returns 0 if reading fails.
262 */
263 uint8_t readByte() noexcept
264 {
265 uint8_t B = 0;
266 return tryRead(&B, sizeof(uint8_t)) ? B : 0;
267 }
268
269 /*
270 * Read a short 16-bit integer.
271 * Returns 0 if reading fails.
272 */
273 int16_t readShort() noexcept
274 {
275 int16_t s = 0;
276 return tryRead(&s, sizeof(int16_t)) ? s : 0;
277 }
278
279 /*
280 * Read a short unsigned 16-bit integer.
281 * Returns 0 if reading fails.
282 */
283 uint16_t readUShort() noexcept
284 {
285 uint16_t us = 0;
286 return tryRead(&us, sizeof(uint16_t)) ? us : 0;
287 }
288
289 /*
290 * Read a regular 32-bit integer.
291 * Returns 0 if reading fails.
292 */
293 int32_t readInt() noexcept
294 {
295 int32_t i = 0;
296 return tryRead(&i, sizeof(int32_t)) ? i : 0;
297 }
298
299 /*
300 * Read an unsigned 32-bit integer.
301 * Returns 0 if reading fails.
302 */
303 uint32_t readUInt() noexcept
304 {
305 uint32_t ui = 0;
306 return tryRead(&ui, sizeof(int32_t)) ? ui : 0;
307 }
308
309 /*
310 * Read a long 64-bit integer.
311 * Returns 0 if reading fails.
312 */
313 int64_t readLong() noexcept
314 {
315 int64_t l = 0;
316 return tryRead(&l, sizeof(int64_t)) ? l : 0;
317 }
318
319 /*
320 * Read a long unsigned 64-bit integer.
321 * Returns 0 if reading fails.
322 */
323 uint64_t readULong() noexcept
324 {
325 uint64_t ul = 0;
326 return tryRead(&ul, sizeof(int64_t)) ? ul : 0;
327 }
328
329 /*
330 * Read a single-precision floating point number.
331 * Returns 0 if reading fails.
332 */
333 float readFloat() noexcept
334 {
335 float f = 0.0f;
336 return tryRead(&f, sizeof(float)) ? f : 0.0f;
337 }
338
339 /*
340 * Read a double-precision floating point number.
341 * Returns 0 if reading fails.
342 */
343 double readDouble() noexcept
344 {
345 double d = 0.0;
346 return tryRead(&d, sizeof(double)) ? d : 0.0;
347 }
348
349 /*!
350 * Read an arbitrary amount of data, specified by @a size.
351 * data pointer must be non-null, and size > 0.
352 *
353 * Returns true if reading succeeds.
354 * In case of failure, @a data pointer is automatically cleared by @a size bytes.
355 */
356 bool readCustomData(void* const data, const uint32_t size) noexcept
357 {
358 DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, false);
359 DISTRHO_SAFE_ASSERT_RETURN(size > 0, false);
360
361 if (tryRead(data, size))
362 return true;
363
364 std::memset(data, 0, size);
365 return false;
366 }
367
368 /*!
369 * Read a custom data type specified by the template typename used,
370 * with size being automatically deduced by the compiler (through the use of sizeof).
371 *
372 * Returns true if reading succeeds.
373 * In case of failure, @a type value is automatically cleared by its deduced size.
374 */
375 template <typename T>
376 bool readCustomType(T& type) noexcept
377 {
378 if (tryRead(&type, sizeof(T)))
379 return true;
380
381 std::memset(&type, 0, sizeof(T));
382 return false;
383 }
384
385 // -------------------------------------------------------------------
386 // write operations
387
388 /*
389 * Write a single boolean value.
390 */
391 bool writeBool(const bool value) noexcept
392 {
393 return tryWrite(&value, sizeof(bool));
394 }
395
396 /*
397 * Write a single 8-bit byte.
398 */
399 bool writeByte(const uint8_t value) noexcept
400 {
401 return tryWrite(&value, sizeof(uint8_t));
402 }
403
404 /*
405 * Write a short 16-bit integer.
406 */
407 bool writeShort(const int16_t value) noexcept
408 {
409 return tryWrite(&value, sizeof(int16_t));
410 }
411
412 /*
413 * Write a short unsigned 16-bit integer.
414 */
415 bool writeUShort(const uint16_t value) noexcept
416 {
417 return tryWrite(&value, sizeof(uint16_t));
418 }
419
420 /*
421 * Write a regular 32-bit integer.
422 */
423 bool writeInt(const int32_t value) noexcept
424 {
425 return tryWrite(&value, sizeof(int32_t));
426 }
427
428 /*
429 * Write an unsigned 32-bit integer.
430 */
431 bool writeUInt(const uint32_t value) noexcept
432 {
433 return tryWrite(&value, sizeof(uint32_t));
434 }
435
436 /*
437 * Write a long 64-bit integer.
438 */
439 bool writeLong(const int64_t value) noexcept
440 {
441 return tryWrite(&value, sizeof(int64_t));
442 }
443
444 /*
445 * Write a long unsigned 64-bit integer.
446 */
447 bool writeULong(const uint64_t value) noexcept
448 {
449 return tryWrite(&value, sizeof(uint64_t));
450 }
451
452 /*
453 * Write a single-precision floating point number.
454 */
455 bool writeFloat(const float value) noexcept
456 {
457 return tryWrite(&value, sizeof(float));
458 }
459
460 /*
461 * Write a double-precision floating point number.
462 */
463 bool writeDouble(const double value) noexcept
464 {
465 return tryWrite(&value, sizeof(double));
466 }
467
468 /*!
469 * Write an arbitrary amount of data, specified by @a size.
470 * data pointer must be non-null, and size > 0.
471 */
472 bool writeCustomData(const void* const data, const uint32_t size) noexcept
473 {
474 DISTRHO_SAFE_ASSERT_RETURN(data != nullptr, false);
475 DISTRHO_SAFE_ASSERT_RETURN(size > 0, false);
476
477 return tryWrite(data, size);
478 }
479
480 /*!
481 * Write a custom data type specified by the template typename used,
482 * with size being automatically deduced by the compiler (through the use of sizeof).
483 */
484 template <typename T>
485 bool writeCustomType(const T& type) noexcept
486 {
487 return tryWrite(&type, sizeof(T));
488 }
489
490 // -------------------------------------------------------------------
491
492 /*!
493 * Commit all previous write operations to the ringbuffer.
494 * If a write operation has previously failed, this will reset/invalidate the previous write attempts.
495 */
496 bool commitWrite() noexcept
497 {
498 DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr, false);
499
500 if (buffer->invalidateCommit)
501 {
502 buffer->wrtn = buffer->head;
503 buffer->invalidateCommit = false;
504 return false;
505 }
506
507 // nothing to commit?
508 DISTRHO_SAFE_ASSERT_RETURN(buffer->head != buffer->wrtn, false);
509
510 // all ok
511 buffer->head = buffer->wrtn;
512 errorWriting = false;
513 return true;
514 }
515
516 // -------------------------------------------------------------------
517
518 /*
519 * Tie this ring buffer control to a ring buffer struct, optionally clearing its data.
520 */
521 void setRingBuffer(BufferStruct* const ringBuf, const bool clearRingBufferData) noexcept
522 {
523 DISTRHO_SAFE_ASSERT_RETURN(buffer != ringBuf,);
524
525 buffer = ringBuf;
526
527 if (clearRingBufferData && ringBuf != nullptr)
528 clearData();
529 }
530
531 // -------------------------------------------------------------------
532
533 protected:
534 /** @internal try reading from the buffer, can fail. */
535 bool tryRead(void* const buf, const uint32_t size) noexcept
536 {
537 DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr, false);
538 #if defined(__clang__)
539 # pragma clang diagnostic push
540 # pragma clang diagnostic ignored "-Wtautological-pointer-compare"
541 #endif
542 DISTRHO_SAFE_ASSERT_RETURN(buffer->buf != nullptr, false);
543 #if defined(__clang__)
544 # pragma clang diagnostic pop
545 #endif
546 DISTRHO_SAFE_ASSERT_RETURN(buf != nullptr, false);
547 DISTRHO_SAFE_ASSERT_RETURN(size > 0, false);
548 DISTRHO_SAFE_ASSERT_RETURN(size < buffer->size, false);
549
550 // empty
551 if (buffer->head == buffer->tail)
552 return false;
553
554 uint8_t* const bytebuf(static_cast<uint8_t*>(buf));
555
556 const uint32_t head(buffer->head);
557 const uint32_t tail(buffer->tail);
558 const uint32_t wrap((head > tail) ? 0 : buffer->size);
559
560 if (size > wrap + head - tail)
561 {
562 if (! errorReading)
563 {
564 errorReading = true;
565 d_stderr2("RingBuffer::tryRead(%p, %lu): failed, not enough space", buf, (ulong)size);
566 }
567 return false;
568 }
569
570 uint32_t readto(tail + size);
571
572 if (readto > buffer->size)
573 {
574 readto -= buffer->size;
575
576 if (size == 1)
577 {
578 std::memcpy(bytebuf, buffer->buf + tail, 1);
579 }
580 else
581 {
582 const uint32_t firstpart(buffer->size - tail);
583 std::memcpy(bytebuf, buffer->buf + tail, firstpart);
584 std::memcpy(bytebuf + firstpart, buffer->buf, readto);
585 }
586 }
587 else
588 {
589 std::memcpy(bytebuf, buffer->buf + tail, size);
590
591 if (readto == buffer->size)
592 readto = 0;
593 }
594
595 buffer->tail = readto;
596 errorReading = false;
597 return true;
598 }
599
600 /** @internal try writing to the buffer, can fail. */
601 bool tryWrite(const void* const buf, const uint32_t size) noexcept
602 {
603 DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr, false);
604 DISTRHO_SAFE_ASSERT_RETURN(buf != nullptr, false);
605 DISTRHO_SAFE_ASSERT_RETURN(size > 0, false);
606 DISTRHO_SAFE_ASSERT_UINT2_RETURN(size < buffer->size, size, buffer->size, false);
607
608 const uint8_t* const bytebuf(static_cast<const uint8_t*>(buf));
609
610 const uint32_t tail(buffer->tail);
611 const uint32_t wrtn(buffer->wrtn);
612 const uint32_t wrap((tail > wrtn) ? 0 : buffer->size);
613
614 if (size >= wrap + tail - wrtn)
615 {
616 if (! errorWriting)
617 {
618 errorWriting = true;
619 d_stderr2("RingBuffer::tryWrite(%p, %lu): failed, not enough space", buf, (ulong)size);
620 }
621 buffer->invalidateCommit = true;
622 return false;
623 }
624
625 uint32_t writeto(wrtn + size);
626
627 if (writeto > buffer->size)
628 {
629 writeto -= buffer->size;
630
631 if (size == 1)
632 {
633 std::memcpy(buffer->buf, bytebuf, 1);
634 }
635 else
636 {
637 const uint32_t firstpart(buffer->size - wrtn);
638 std::memcpy(buffer->buf + wrtn, bytebuf, firstpart);
639 std::memcpy(buffer->buf, bytebuf + firstpart, writeto);
640 }
641 }
642 else
643 {
644 std::memcpy(buffer->buf + wrtn, bytebuf, size);
645
646 if (writeto == buffer->size)
647 writeto = 0;
648 }
649
650 buffer->wrtn = writeto;
651 return true;
652 }
653
654 private:
655 /** Buffer struct pointer. */
656 BufferStruct* buffer;
657
658 /** Whether read errors have been printed to terminal. */
659 bool errorReading;
660
661 /** Whether write errors have been printed to terminal. */
662 bool errorWriting;
663
664 DISTRHO_PREVENT_VIRTUAL_HEAP_ALLOCATION
665 DISTRHO_DECLARE_NON_COPYABLE(RingBufferControl)
666 };
667
668 template <class BufferStruct>
669 inline bool RingBufferControl<BufferStruct>::isDataAvailableForReading() const noexcept
670 {
671 return (buffer != nullptr && buffer->head != buffer->tail);
672 }
673
674 template <>
675 inline bool RingBufferControl<HeapBuffer>::isDataAvailableForReading() const noexcept
676 {
677 return (buffer != nullptr && buffer->buf != nullptr && buffer->head != buffer->tail);
678 }
679
680 // -----------------------------------------------------------------------
681 // RingBuffer using heap space
682
683 /**
684 RingBufferControl with a heap buffer.
685 This is a convenience class that provides a method for creating and destroying the heap data.
686 Requires the use of createBuffer(uint32_t) to make the ring buffer usable.
687 */
688 class HeapRingBuffer : public RingBufferControl<HeapBuffer>
689 {
690 public:
691 /** Constructor. */
692 HeapRingBuffer() noexcept
693 : heapBuffer(HeapBuffer_INIT)
694 {
695 #ifndef DISTRHO_PROPER_CPP11_SUPPORT
696 std::memset(&heapBuffer, 0, sizeof(heapBuffer));
697 #endif
698 }
699
700 /** Destructor. */
701 ~HeapRingBuffer() noexcept override
702 {
703 if (heapBuffer.buf == nullptr)
704 return;
705
706 delete[] heapBuffer.buf;
707 heapBuffer.buf = nullptr;
708 }
709
710 /** Create a buffer of the specified size. */
711 bool createBuffer(const uint32_t size) noexcept
712 {
713 DISTRHO_SAFE_ASSERT_RETURN(heapBuffer.buf == nullptr, false);
714 DISTRHO_SAFE_ASSERT_RETURN(size > 0, false);
715
716 const uint32_t p2size = d_nextPowerOf2(size);
717
718 try {
719 heapBuffer.buf = new uint8_t[p2size];
720 } DISTRHO_SAFE_EXCEPTION_RETURN("HeapRingBuffer::createBuffer", false);
721
722 heapBuffer.size = p2size;
723 setRingBuffer(&heapBuffer, true);
724 return true;
725 }
726
727 /** Delete the previously allocated buffer. */
728 void deleteBuffer() noexcept
729 {
730 DISTRHO_SAFE_ASSERT_RETURN(heapBuffer.buf != nullptr,);
731
732 setRingBuffer(nullptr, false);
733
734 delete[] heapBuffer.buf;
735 heapBuffer.buf = nullptr;
736 heapBuffer.size = 0;
737 }
738
739 void copyFromAndClearOther(HeapRingBuffer& other)
740 {
741 DISTRHO_SAFE_ASSERT_RETURN(other.heapBuffer.size == heapBuffer.size,);
742
743 std::memcpy(&heapBuffer, &other.heapBuffer, sizeof(HeapBuffer) - sizeof(uint8_t*));
744 std::memcpy(heapBuffer.buf, other.heapBuffer.buf, sizeof(uint8_t) * heapBuffer.size);
745 other.clearData();
746 }
747
748 private:
749 /** The heap buffer used for this class. */
750 HeapBuffer heapBuffer;
751
752 DISTRHO_PREVENT_VIRTUAL_HEAP_ALLOCATION
753 DISTRHO_DECLARE_NON_COPYABLE(HeapRingBuffer)
754 };
755
756 // -----------------------------------------------------------------------
757 // RingBuffer using small stack space
758
759 /**
760 RingBufferControl with an included small stack buffer.
761 No setup is necessary, this class is usable as-is.
762 */
763 class SmallStackRingBuffer : public RingBufferControl<SmallStackBuffer>
764 {
765 public:
766 /** Constructor. */
767 SmallStackRingBuffer() noexcept
768 : stackBuffer(StackBuffer_INIT)
769 {
770 #ifndef DISTRHO_PROPER_CPP11_SUPPORT
771 std::memset(&stackBuffer, 0, sizeof(stackBuffer));
772 #endif
773 setRingBuffer(&stackBuffer, true);
774 }
775
776 private:
777 /** The small stack buffer used for this class. */
778 SmallStackBuffer stackBuffer;
779
780 DISTRHO_PREVENT_VIRTUAL_HEAP_ALLOCATION
781 DISTRHO_DECLARE_NON_COPYABLE(SmallStackRingBuffer)
782 };
783
784 // -----------------------------------------------------------------------
785
786 END_NAMESPACE_DISTRHO
787
788 #endif // DISTRHO_RING_BUFFER_HPP_INCLUDED