Dusk Audio Framework
Loading...
Searching...
No Matches
RingBuffer.hpp
1/*
2 * DISTRHO Plugin Framework (DPF)
3 * Copyright (C) 2012-2024 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 DAF_RING_BUFFER_HPP_INCLUDED
18#define DAF_RING_BUFFER_HPP_INCLUDED
19
20#include "../DafUtils.hpp"
21
23
24// -----------------------------------------------------------------------
25// Buffer structs
26
27/**
28 Base structure for all RingBuffer containers.
29 This struct details the data model used in DAF's RingBuffer class.
30
31 DAF 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 */
49struct 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 */
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*/
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*/
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*/
116 static const uint32_t size = 65536;
117 uint32_t head, tail, wrtn;
118 bool invalidateCommit;
119 uint8_t buf[size];
120};
121
122// -----------------------------------------------------------------------
123// RingBufferControl templated class
124
125/**
126 DAF built-in RingBuffer class.
127 RingBufferControl takes one buffer struct to take control over, and operates over it.
128
129 This is meant for single-writer, single-reader type of control.
130 Writing and reading is wait and lock-free.
131
132 Typically usage involves:
133 ```
134 // definition
135 HeapRingBuffer myHeapBuffer; // or RingBufferControl<HeapBuffer> class for more control
136
137 // construction, only needed for heap buffers
138 myHeapBuffer.createBuffer(8192);
139
140 // writing data
141 myHeapBuffer.writeUInt(size);
142 myHeapBuffer.writeCustomData(someOtherData, size);
143 myHeapBuffer.commitWrite();
144
145 // reading data
146 if (myHeapBuffer.isDataAvailableForReading())
147 {
148 uint32_t size;
149 if (myHeapBuffer.readUInt(size) && readCustomData(&anotherData, size))
150 {
151 // do something with "anotherData"
152 }
153 }
154 ```
155
156 @see HeapBuffer
157 */
158template <class BufferStruct>
159class RingBufferControl
160{
161public:
162 /*
163 * Constructor for uninitialised ring buffer.
164 * A call to setRingBuffer is required to tied this control to a ring buffer struct;
165 *
166 */
167 RingBufferControl() noexcept
168 : buffer(nullptr),
169 errorReading(false),
170 errorWriting(false) {}
171
172 /*
173 * Destructor.
174 */
175 virtual ~RingBufferControl() noexcept {}
176
177 // -------------------------------------------------------------------
178 // check operations
179
180 /*
181 * Check if there is any data available for reading, regardless of size.
182 */
183 bool isDataAvailableForReading() const noexcept;
184
185 /*
186 * Check if ring buffer is empty (that is, there is nothing to read).
187 */
188 bool isEmpty() const noexcept
189 {
190 DAF_SAFE_ASSERT_RETURN(buffer != nullptr, false);
191
192 return (buffer->buf == nullptr || buffer->head == buffer->tail);
193 }
194
195 /*
196 * Get the full ringbuffer size.
197 */
198 uint32_t getSize() const noexcept
199 {
200 return buffer != nullptr ? buffer->size : 0;
201 }
202
203 /*
204 * Get the size of the data available to read.
205 */
206 uint32_t getReadableDataSize() const noexcept
207 {
208 DAF_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 DAF_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 - 1;
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 DAF_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 * Reset the ring buffer read and write positions, marking the buffer as empty.
248 * Requires a buffer struct tied to this class.
249 */
250 void flush() noexcept
251 {
252 DAF_SAFE_ASSERT_RETURN(buffer != nullptr,);
253
254 buffer->head = buffer->tail = buffer->wrtn = 0;
255 buffer->invalidateCommit = false;
256
257 errorWriting = false;
258 }
259
260 // -------------------------------------------------------------------
261 // read operations
262
263 /*
264 * Read a single boolean value.
265 * Returns false if reading fails.
266 */
267 bool readBool() noexcept
268 {
269 bool b = false;
270 return tryRead(&b, sizeof(bool)) ? b : false;
271 }
272
273 /*
274 * Read a single 8-bit byte.
275 * Returns 0 if reading fails.
276 */
277 uint8_t readByte() noexcept
278 {
279 uint8_t B = 0;
280 return tryRead(&B, sizeof(uint8_t)) ? B : 0;
281 }
282
283 /*
284 * Read a short 16-bit integer.
285 * Returns 0 if reading fails.
286 */
287 int16_t readShort() noexcept
288 {
289 int16_t s = 0;
290 return tryRead(&s, sizeof(int16_t)) ? s : 0;
291 }
292
293 /*
294 * Read a short unsigned 16-bit integer.
295 * Returns 0 if reading fails.
296 */
297 uint16_t readUShort() noexcept
298 {
299 uint16_t us = 0;
300 return tryRead(&us, sizeof(uint16_t)) ? us : 0;
301 }
302
303 /*
304 * Read a regular 32-bit integer.
305 * Returns 0 if reading fails.
306 */
307 int32_t readInt() noexcept
308 {
309 int32_t i = 0;
310 return tryRead(&i, sizeof(int32_t)) ? i : 0;
311 }
312
313 /*
314 * Read an unsigned 32-bit integer.
315 * Returns 0 if reading fails.
316 */
317 uint32_t readUInt() noexcept
318 {
319 uint32_t ui = 0;
320 return tryRead(&ui, sizeof(int32_t)) ? ui : 0;
321 }
322
323 /*
324 * Read a long 64-bit integer.
325 * Returns 0 if reading fails.
326 */
327 int64_t readLong() noexcept
328 {
329 int64_t l = 0;
330 return tryRead(&l, sizeof(int64_t)) ? l : 0;
331 }
332
333 /*
334 * Read a long unsigned 64-bit integer.
335 * Returns 0 if reading fails.
336 */
337 uint64_t readULong() noexcept
338 {
339 uint64_t ul = 0;
340 return tryRead(&ul, sizeof(int64_t)) ? ul : 0;
341 }
342
343 /*
344 * Read a single-precision floating point number.
345 * Returns 0 if reading fails.
346 */
347 float readFloat() noexcept
348 {
349 float f = 0.0f;
350 return tryRead(&f, sizeof(float)) ? f : 0.0f;
351 }
352
353 /*
354 * Read a double-precision floating point number.
355 * Returns 0 if reading fails.
356 */
357 double readDouble() noexcept
358 {
359 double d = 0.0;
360 return tryRead(&d, sizeof(double)) ? d : 0.0;
361 }
362
363 /*!
364 * Read an arbitrary amount of data, specified by @a size.
365 * data pointer must be non-null, and size > 0.
366 *
367 * Returns true if reading succeeds.
368 * In case of failure, @a data pointer is automatically cleared by @a size bytes.
369 */
370 bool readCustomData(void* const data, const uint32_t size) noexcept
371 {
372 DAF_SAFE_ASSERT_RETURN(data != nullptr, false);
373 DAF_SAFE_ASSERT_RETURN(size > 0, false);
374
375 if (tryRead(data, size))
376 return true;
377
378 std::memset(data, 0, size);
379 return false;
380 }
381
382 /*!
383 * Read a custom data type specified by the template typename used,
384 * with size being automatically deduced by the compiler (through the use of sizeof).
385 *
386 * Returns true if reading succeeds.
387 * In case of failure, @a type value is automatically cleared by its deduced size.
388 */
389 template <typename T>
390 bool readCustomType(T& type) noexcept
391 {
392 if (tryRead(&type, sizeof(T)))
393 return true;
394
395 std::memset(&type, 0, sizeof(T));
396 return false;
397 }
398
399 // -------------------------------------------------------------------
400 // peek operations (returns a value without advancing read position)
401
402 /*
403 * Peek for an unsigned 32-bit integer.
404 * Returns 0 if reading fails.
405 */
406 uint32_t peekUInt() const noexcept
407 {
408 uint32_t ui = 0;
409 return tryPeek(&ui, sizeof(int32_t)) ? ui : 0;
410 }
411
412 /*!
413 * Peek for a custom data type specified by the template typename used,
414 * with size being automatically deduced by the compiler (through the use of sizeof).
415 *
416 * Returns true if peeking succeeds.
417 * In case of failure, @a type value is automatically cleared by its deduced size.
418 */
419 template <typename T>
420 bool peekCustomType(T& type) const noexcept
421 {
422 if (tryPeek(&type, sizeof(T)))
423 return true;
424
425 std::memset(&type, 0, sizeof(T));
426 return false;
427 }
428
429 // -------------------------------------------------------------------
430 // write operations
431
432 /*
433 * Write a single boolean value.
434 */
435 bool writeBool(const bool value) noexcept
436 {
437 return tryWrite(&value, sizeof(bool));
438 }
439
440 /*
441 * Write a single 8-bit byte.
442 */
443 bool writeByte(const uint8_t value) noexcept
444 {
445 return tryWrite(&value, sizeof(uint8_t));
446 }
447
448 /*
449 * Write a short 16-bit integer.
450 */
451 bool writeShort(const int16_t value) noexcept
452 {
453 return tryWrite(&value, sizeof(int16_t));
454 }
455
456 /*
457 * Write a short unsigned 16-bit integer.
458 */
459 bool writeUShort(const uint16_t value) noexcept
460 {
461 return tryWrite(&value, sizeof(uint16_t));
462 }
463
464 /*
465 * Write a regular 32-bit integer.
466 */
467 bool writeInt(const int32_t value) noexcept
468 {
469 return tryWrite(&value, sizeof(int32_t));
470 }
471
472 /*
473 * Write an unsigned 32-bit integer.
474 */
475 bool writeUInt(const uint32_t value) noexcept
476 {
477 return tryWrite(&value, sizeof(uint32_t));
478 }
479
480 /*
481 * Write a long 64-bit integer.
482 */
483 bool writeLong(const int64_t value) noexcept
484 {
485 return tryWrite(&value, sizeof(int64_t));
486 }
487
488 /*
489 * Write a long unsigned 64-bit integer.
490 */
491 bool writeULong(const uint64_t value) noexcept
492 {
493 return tryWrite(&value, sizeof(uint64_t));
494 }
495
496 /*
497 * Write a single-precision floating point number.
498 */
499 bool writeFloat(const float value) noexcept
500 {
501 return tryWrite(&value, sizeof(float));
502 }
503
504 /*
505 * Write a double-precision floating point number.
506 */
507 bool writeDouble(const double value) noexcept
508 {
509 return tryWrite(&value, sizeof(double));
510 }
511
512 /*!
513 * Write an arbitrary amount of data, specified by @a size.
514 * data pointer must be non-null, and size > 0.
515 */
516 bool writeCustomData(const void* const data, const uint32_t size) noexcept
517 {
518 DAF_SAFE_ASSERT_RETURN(data != nullptr, false);
519 DAF_SAFE_ASSERT_RETURN(size > 0, false);
520
521 return tryWrite(data, size);
522 }
523
524 /*!
525 * Write a custom data type specified by the template typename used,
526 * with size being automatically deduced by the compiler (through the use of sizeof).
527 */
528 template <typename T>
529 bool writeCustomType(const T& type) noexcept
530 {
531 return tryWrite(&type, sizeof(T));
532 }
533
534 // -------------------------------------------------------------------
535
536 /*!
537 * Commit all previous write operations to the ringbuffer.
538 * If a write operation has previously failed, this will reset/invalidate the previous write attempts.
539 */
540 bool commitWrite(const char* const debugMsg = nullptr) noexcept
541 {
542 DAF_SAFE_ASSERT_RETURN(buffer != nullptr, false);
543
544 if (buffer->invalidateCommit)
545 {
546 buffer->wrtn = buffer->head;
547 buffer->invalidateCommit = false;
548 return false;
549 }
550
551 // nothing to commit?
552 if (debugMsg != nullptr) {
553 DAF_CUSTOM_SAFE_ASSERT_RETURN(debugMsg, buffer->head != buffer->wrtn, false);
554 } else {
555 DAF_SAFE_ASSERT_RETURN(buffer->head != buffer->wrtn, false);
556 }
557
558 // all ok
559 buffer->head = buffer->wrtn;
560 errorWriting = false;
561 return true;
562 }
563
564 // -------------------------------------------------------------------
565
566 /*
567 * Tie this ring buffer control to a ring buffer struct, optionally clearing its data.
568 */
569 void setRingBuffer(BufferStruct* const ringBuf, const bool clearRingBufferData) noexcept
570 {
571 DAF_SAFE_ASSERT_RETURN(buffer != ringBuf,);
572
573 buffer = ringBuf;
574
575 if (clearRingBufferData && ringBuf != nullptr)
576 clearData();
577 }
578
579 // -------------------------------------------------------------------
580
581protected:
582 /** @internal try reading from the buffer, can fail. */
583 bool tryRead(void* const buf, const uint32_t size) noexcept
584 {
585 DAF_SAFE_ASSERT_RETURN(buffer != nullptr, false);
586 #if defined(__clang__)
587 #pragma clang diagnostic push
588 #pragma clang diagnostic ignored "-Wtautological-pointer-compare"
589 #endif
590 DAF_SAFE_ASSERT_RETURN(buffer->buf != nullptr, false);
591 #if defined(__clang__)
592 #pragma clang diagnostic pop
593 #endif
594 DAF_SAFE_ASSERT_RETURN(buf != nullptr, false);
595 DAF_SAFE_ASSERT_RETURN(size > 0, false);
596 DAF_SAFE_ASSERT_RETURN(size < buffer->size, false);
597
598 // empty
599 if (buffer->head == buffer->tail)
600 return false;
601
602 uint8_t* const bytebuf = static_cast<uint8_t*>(buf);
603
604 const uint32_t head = buffer->head;
605 const uint32_t tail = buffer->tail;
606 const uint32_t wrap = head > tail ? 0 : buffer->size;
607
608 if (size > wrap + head - tail)
609 {
610 if (! errorReading)
611 {
612 errorReading = true;
613 d_stderr2("RingBuffer::tryRead(%p, %lu): failed, not enough space", buf, (ulong)size);
614 }
615 return false;
616 }
617
618 uint32_t readto = tail + size;
619
620 if (readto > buffer->size)
621 {
622 readto -= buffer->size;
623
624 if (size == 1)
625 {
626 std::memcpy(bytebuf, buffer->buf + tail, 1);
627 }
628 else
629 {
630 const uint32_t firstpart = buffer->size - tail;
631 std::memcpy(bytebuf, buffer->buf + tail, firstpart);
632 std::memcpy(bytebuf + firstpart, buffer->buf, readto);
633 }
634 }
635 else
636 {
637 std::memcpy(bytebuf, buffer->buf + tail, size);
638
639 if (readto == buffer->size)
640 readto = 0;
641 }
642
643 buffer->tail = readto;
644 errorReading = false;
645 return true;
646 }
647
648 /** @internal try reading from the buffer, can fail. */
649 bool tryPeek(void* const buf, const uint32_t size) const noexcept
650 {
651 DAF_SAFE_ASSERT_RETURN(buffer != nullptr, false);
652 #if defined(__clang__)
653 #pragma clang diagnostic push
654 #pragma clang diagnostic ignored "-Wtautological-pointer-compare"
655 #endif
656 DAF_SAFE_ASSERT_RETURN(buffer->buf != nullptr, false);
657 #if defined(__clang__)
658 #pragma clang diagnostic pop
659 #endif
660 DAF_SAFE_ASSERT_RETURN(buf != nullptr, false);
661 DAF_SAFE_ASSERT_RETURN(size > 0, false);
662 DAF_SAFE_ASSERT_RETURN(size < buffer->size, false);
663
664 // empty
665 if (buffer->head == buffer->tail)
666 return false;
667
668 uint8_t* const bytebuf = static_cast<uint8_t*>(buf);
669
670 const uint32_t head = buffer->head;
671 const uint32_t tail = buffer->tail;
672 const uint32_t wrap = head > tail ? 0 : buffer->size;
673
674 if (size > wrap + head - tail)
675 return false;
676
677 uint32_t readto = tail + size;
678
679 if (readto > buffer->size)
680 {
681 readto -= buffer->size;
682
683 if (size == 1)
684 {
685 std::memcpy(bytebuf, buffer->buf + tail, 1);
686 }
687 else
688 {
689 const uint32_t firstpart = buffer->size - tail;
690 std::memcpy(bytebuf, buffer->buf + tail, firstpart);
691 std::memcpy(bytebuf + firstpart, buffer->buf, readto);
692 }
693 }
694 else
695 {
696 std::memcpy(bytebuf, buffer->buf + tail, size);
697
698 if (readto == buffer->size)
699 readto = 0;
700 }
701
702 return true;
703 }
704
705 /** @internal try writing to the buffer, can fail. */
706 bool tryWrite(const void* const buf, const uint32_t size) noexcept
707 {
708 DAF_SAFE_ASSERT_RETURN(buffer != nullptr, false);
709 DAF_SAFE_ASSERT_RETURN(buf != nullptr, false);
710 DAF_SAFE_ASSERT_RETURN(size > 0, false);
711 DAF_SAFE_ASSERT_UINT2_RETURN(size < buffer->size, size, buffer->size, false);
712
713 const uint8_t* const bytebuf = static_cast<const uint8_t*>(buf);
714
715 const uint32_t tail = buffer->tail;
716 const uint32_t wrtn = buffer->wrtn;
717 const uint32_t wrap = tail > wrtn ? 0 : buffer->size;
718
719 if (size >= wrap + tail - wrtn)
720 {
721 if (! errorWriting)
722 {
723 errorWriting = true;
724 d_stderr2("RingBuffer::tryWrite(%p, %lu): failed, not enough space", buf, (ulong)size);
725 }
726 buffer->invalidateCommit = true;
727 return false;
728 }
729
730 uint32_t writeto = wrtn + size;
731
732 if (writeto > buffer->size)
733 {
734 writeto -= buffer->size;
735
736 if (size == 1)
737 {
738 std::memcpy(buffer->buf, bytebuf, 1);
739 }
740 else
741 {
742 const uint32_t firstpart = buffer->size - wrtn;
743 std::memcpy(buffer->buf + wrtn, bytebuf, firstpart);
744 std::memcpy(buffer->buf, bytebuf + firstpart, writeto);
745 }
746 }
747 else
748 {
749 std::memcpy(buffer->buf + wrtn, bytebuf, size);
750
751 if (writeto == buffer->size)
752 writeto = 0;
753 }
754
755 buffer->wrtn = writeto;
756 return true;
757 }
758
759private:
760 /** Buffer struct pointer. */
761 BufferStruct* buffer;
762
763 /** Whether read errors have been printed to terminal. */
764 bool errorReading;
765
766 /** Whether write errors have been printed to terminal. */
767 bool errorWriting;
768
769 DAF_PREVENT_VIRTUAL_HEAP_ALLOCATION
770 DAF_DECLARE_NON_COPYABLE(RingBufferControl)
771};
772
773template <class BufferStruct>
774inline bool RingBufferControl<BufferStruct>::isDataAvailableForReading() const noexcept
775{
776 return (buffer != nullptr && buffer->head != buffer->tail);
777}
778
779template <>
780inline bool RingBufferControl<HeapBuffer>::isDataAvailableForReading() const noexcept
781{
782 return (buffer != nullptr && buffer->buf != nullptr && buffer->head != buffer->tail);
783}
784
785// -----------------------------------------------------------------------
786// RingBuffer using heap space
787
788/**
789 RingBufferControl with a heap buffer.
790 This is a convenience class that provides a method for creating and destroying the heap data.
791 Requires the use of createBuffer(uint32_t) to make the ring buffer usable.
792*/
793class HeapRingBuffer : public RingBufferControl<HeapBuffer>
794{
795public:
796 /** Constructor. */
797 HeapRingBuffer() noexcept
798 : heapBuffer(CPP_AGGREGATE_INIT(HeapBuffer){0, 0, 0, 0, false, nullptr}) {}
799
800 /** Destructor. */
801 ~HeapRingBuffer() noexcept override
802 {
803 if (heapBuffer.buf == nullptr)
804 return;
805
806 delete[] heapBuffer.buf;
807 heapBuffer.buf = nullptr;
808 }
809
810 /** Create a buffer of the specified size. */
811 bool createBuffer(const uint32_t size) noexcept
812 {
813 DAF_SAFE_ASSERT_RETURN(heapBuffer.buf == nullptr, false);
814 DAF_SAFE_ASSERT_RETURN(size > 0, false);
815
816 const uint32_t p2size = d_nextPowerOf2(size);
817
818 try {
819 heapBuffer.buf = new uint8_t[p2size];
820 } DAF_SAFE_EXCEPTION_RETURN("HeapRingBuffer::createBuffer", false);
821
822 heapBuffer.size = p2size;
823 setRingBuffer(&heapBuffer, true);
824 return true;
825 }
826
827 /** Delete the previously allocated buffer. */
828 void deleteBuffer() noexcept
829 {
830 DAF_SAFE_ASSERT_RETURN(heapBuffer.buf != nullptr,);
831
832 setRingBuffer(nullptr, false);
833
834 delete[] heapBuffer.buf;
835 heapBuffer.buf = nullptr;
836 heapBuffer.size = 0;
837 }
838
839 void copyFromAndClearOther(HeapRingBuffer& other)
840 {
841 DAF_SAFE_ASSERT_RETURN(other.heapBuffer.size == heapBuffer.size,);
842
843 std::memcpy(&heapBuffer, &other.heapBuffer, sizeof(HeapBuffer) - sizeof(uint8_t*));
844 std::memcpy(heapBuffer.buf, other.heapBuffer.buf, sizeof(uint8_t) * heapBuffer.size);
845 other.clearData();
846 }
847
848private:
849 /** The heap buffer used for this class. */
850 HeapBuffer heapBuffer;
851
852 DAF_PREVENT_VIRTUAL_HEAP_ALLOCATION
853 DAF_DECLARE_NON_COPYABLE(HeapRingBuffer)
854};
855
856// -----------------------------------------------------------------------
857// RingBuffer using small stack space
858
859/**
860 RingBufferControl with an included small stack buffer.
861 No setup is necessary, this class is usable as-is.
862*/
863class SmallStackRingBuffer : public RingBufferControl<SmallStackBuffer>
864{
865public:
866 /** Constructor. */
868 : stackBuffer(CPP_AGGREGATE_INIT(SmallStackBuffer){0, 0, 0, false, {0}})
869 {
870 setRingBuffer(&stackBuffer, true);
871 }
872
873private:
874 /** The small stack buffer used for this class. */
875 SmallStackBuffer stackBuffer;
876
877 DAF_PREVENT_VIRTUAL_HEAP_ALLOCATION
878 DAF_DECLARE_NON_COPYABLE(SmallStackRingBuffer)
879};
880
881// -----------------------------------------------------------------------
882
884
885#endif // DAF_RING_BUFFER_HPP_INCLUDED
Definition RingBuffer.hpp:794
HeapRingBuffer() noexcept
Definition RingBuffer.hpp:797
void deleteBuffer() noexcept
Definition RingBuffer.hpp:828
bool createBuffer(const uint32_t size) noexcept
Definition RingBuffer.hpp:811
~HeapRingBuffer() noexcept override
Definition RingBuffer.hpp:801
bool writeCustomType(const T &type) noexcept
Definition RingBuffer.hpp:529
bool commitWrite(const char *const debugMsg=nullptr) noexcept
Definition RingBuffer.hpp:540
bool writeCustomData(const void *const data, const uint32_t size) noexcept
Definition RingBuffer.hpp:516
bool readCustomType(T &type) noexcept
Definition RingBuffer.hpp:390
bool readCustomData(void *const data, const uint32_t size) noexcept
Definition RingBuffer.hpp:370
bool peekCustomType(T &type) const noexcept
Definition RingBuffer.hpp:420
SmallStackRingBuffer() noexcept
Definition RingBuffer.hpp:867
#define END_NAMESPACE_DAF
Definition DafInfo.hpp:996
#define START_NAMESPACE_DAF
Definition DafInfo.hpp:990
Definition RingBuffer.hpp:104
Definition RingBuffer.hpp:49
bool invalidateCommit
Definition RingBuffer.hpp:80
uint32_t wrtn
Definition RingBuffer.hpp:74
uint32_t head
Definition RingBuffer.hpp:60
uint32_t size
Definition RingBuffer.hpp:54
uint32_t tail
Definition RingBuffer.hpp:67
uint8_t * buf
Definition RingBuffer.hpp:86
Definition RingBuffer.hpp:115
Definition RingBuffer.hpp:93