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 3.7KB

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