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.

ringbuffer.hpp 4.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. #pragma once
  2. #include <string.h>
  3. #include "util/common.hpp"
  4. namespace rack {
  5. /** A simple cyclic buffer.
  6. S must be a power of 2.
  7. Thread-safe for single producers and consumers.
  8. */
  9. template <typename T, size_t S>
  10. struct RingBuffer {
  11. T data[S];
  12. size_t start = 0;
  13. size_t end = 0;
  14. size_t mask(size_t i) const {
  15. return i & (S - 1);
  16. }
  17. void push(T t) {
  18. size_t i = mask(end++);
  19. data[i] = t;
  20. }
  21. void pushBuffer(const T *t, int n) {
  22. size_t i = mask(end);
  23. size_t e1 = i + n;
  24. size_t e2 = (e1 < S) ? e1 : S;
  25. memcpy(&data[i], t, sizeof(T) * (e2 - i));
  26. if (e1 > S) {
  27. memcpy(data, &t[S - i], sizeof(T) * (e1 - S));
  28. }
  29. end += n;
  30. }
  31. T shift() {
  32. return data[mask(start++)];
  33. }
  34. void shiftBuffer(T *t, size_t n) {
  35. size_t i = mask(start);
  36. size_t s1 = i + n;
  37. size_t s2 = (s1 < S) ? s1 : S;
  38. memcpy(t, &data[i], sizeof(T) * (s2 - i));
  39. if (s1 > S) {
  40. memcpy(&t[S - i], data, sizeof(T) * (s1 - S));
  41. }
  42. start += n;
  43. }
  44. void clear() {
  45. start = end;
  46. }
  47. bool empty() const {
  48. return start == end;
  49. }
  50. bool full() const {
  51. return end - start == S;
  52. }
  53. size_t size() const {
  54. return end - start;
  55. }
  56. size_t capacity() const {
  57. return S - size();
  58. }
  59. };
  60. /** A cyclic buffer which maintains a valid linear array of size S by keeping a copy of the buffer in adjacent memory.
  61. S must be a power of 2.
  62. Thread-safe for single producers and consumers?
  63. */
  64. template <typename T, size_t S>
  65. struct DoubleRingBuffer {
  66. T data[S*2];
  67. size_t start = 0;
  68. size_t end = 0;
  69. size_t mask(size_t i) const {
  70. return i & (S - 1);
  71. }
  72. void push(T t) {
  73. size_t i = mask(end++);
  74. data[i] = t;
  75. data[i + S] = t;
  76. }
  77. T shift() {
  78. return data[mask(start++)];
  79. }
  80. void clear() {
  81. start = end;
  82. }
  83. bool empty() const {
  84. return start == end;
  85. }
  86. bool full() const {
  87. return end - start == S;
  88. }
  89. size_t size() const {
  90. return end - start;
  91. }
  92. size_t capacity() const {
  93. return S - size();
  94. }
  95. /** Returns a pointer to S consecutive elements for appending.
  96. If any data is appended, you must call endIncr afterwards.
  97. Pointer is invalidated when any other method is called.
  98. */
  99. T *endData() {
  100. return &data[mask(end)];
  101. }
  102. void endIncr(size_t n) {
  103. size_t e = mask(end);
  104. size_t e1 = e + n;
  105. size_t e2 = (e1 < S) ? e1 : S;
  106. // Copy data forward
  107. memcpy(&data[S + e], &data[e], sizeof(T) * (e2 - e));
  108. if (e1 > S) {
  109. // Copy data backward from the doubled block to the main block
  110. memcpy(data, &data[S], sizeof(T) * (e1 - S));
  111. }
  112. end += n;
  113. }
  114. /** Returns a pointer to S consecutive elements for consumption
  115. If any data is consumed, call startIncr afterwards.
  116. */
  117. const T *startData() const {
  118. return &data[mask(start)];
  119. }
  120. void startIncr(size_t n) {
  121. start += n;
  122. }
  123. };
  124. /** A cyclic buffer which maintains a valid linear array of size S by sliding along a larger block of size N.
  125. The linear array of S elements are moved back to the start of the block once it outgrows past the end.
  126. This happens every N - S pushes, so the push() time is O(1 + S / (N - S)).
  127. For example, a float buffer of size 64 in a block of size 1024 is nearly as efficient as RingBuffer.
  128. Not thread-safe.
  129. */
  130. template <typename T, size_t S, size_t N>
  131. struct AppleRingBuffer {
  132. T data[N];
  133. size_t start = 0;
  134. size_t end = 0;
  135. void returnBuffer() {
  136. // move end block to beginning
  137. // may overlap, but memmove handles that correctly
  138. size_t s = size();
  139. memmove(data, &data[start], sizeof(T) * s);
  140. start = 0;
  141. end = s;
  142. }
  143. void push(T t) {
  144. if (end + 1 > N) {
  145. returnBuffer();
  146. }
  147. data[end++] = t;
  148. }
  149. T shift() {
  150. return data[start++];
  151. }
  152. bool empty() const {
  153. return start == end;
  154. }
  155. bool full() const {
  156. return end - start == S;
  157. }
  158. size_t size() const {
  159. return end - start;
  160. }
  161. size_t capacity() const {
  162. return S - size();
  163. }
  164. /** Returns a pointer to S consecutive elements for appending, requesting to append n elements.
  165. */
  166. T *endData(size_t n) {
  167. if (end + n > N) {
  168. returnBuffer();
  169. }
  170. return &data[end];
  171. }
  172. /** Actually increments the end position
  173. Must be called after endData(), and `n` must be at most the `n` passed to endData()
  174. */
  175. void endIncr(size_t n) {
  176. end += n;
  177. }
  178. /** Returns a pointer to S consecutive elements for consumption
  179. If any data is consumed, call startIncr afterwards.
  180. */
  181. const T *startData() const {
  182. return &data[start];
  183. }
  184. void startIncr(size_t n) {
  185. // This is valid as long as n < S
  186. start += n;
  187. }
  188. };
  189. } // namespace rack