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