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.

898 lines
25KB

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