DISTRHO Plugin Framework
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

886 lines
25KB

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