RingBuffer.hpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. /*
  2. * ZeroTier One - Network Virtualization Everywhere
  3. * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. * --
  19. *
  20. * You can be released from the requirements of the license by purchasing
  21. * a commercial license. Buying such a license is mandatory as soon as you
  22. * develop commercial closed-source software that incorporates or links
  23. * directly against ZeroTier software without disclosing the source code
  24. * of your own application.
  25. */
  26. #ifndef ZT_RINGBUFFER_H
  27. #define ZT_RINGBUFFER_H
  28. #include <typeinfo>
  29. #include <cstdint>
  30. #include <stdlib.h>
  31. #include <memory.h>
  32. #include <algorithm>
  33. #include <math.h>
  34. namespace ZeroTier {
  35. /**
  36. * A circular buffer
  37. *
  38. * For fast handling of continuously-evolving variables (such as path quality metrics).
  39. * Using this, we can maintain longer sliding historical windows for important path
  40. * metrics without the need for potentially expensive calls to memcpy/memmove.
  41. *
  42. * Some basic statistical functionality is implemented here in an attempt
  43. * to reduce the complexity of code needed to interact with this type of buffer.
  44. */
  45. template <class T,size_t S>
  46. class RingBuffer
  47. {
  48. private:
  49. T buf[S];
  50. size_t begin;
  51. size_t end;
  52. bool wrap;
  53. public:
  54. RingBuffer() :
  55. begin(0),
  56. end(0),
  57. wrap(false)
  58. {
  59. memset(buf,0,sizeof(T)*S);
  60. }
  61. /**
  62. * @return A pointer to the underlying buffer
  63. */
  64. inline T *get_buf()
  65. {
  66. return buf + begin;
  67. }
  68. /**
  69. * Adjust buffer index pointer as if we copied data in
  70. * @param n Number of elements to copy in
  71. * @return Number of elements we copied in
  72. */
  73. inline size_t produce(size_t n)
  74. {
  75. n = std::min(n, getFree());
  76. if (n == 0) {
  77. return n;
  78. }
  79. const size_t first_chunk = std::min(n, S - end);
  80. end = (end + first_chunk) % S;
  81. if (first_chunk < n) {
  82. const size_t second_chunk = n - first_chunk;
  83. end = (end + second_chunk) % S;
  84. }
  85. if (begin == end) {
  86. wrap = true;
  87. }
  88. return n;
  89. }
  90. /**
  91. * Fast erase, O(1).
  92. * Merely reset the buffer pointer, doesn't erase contents
  93. */
  94. inline void reset() { consume(count()); }
  95. /**
  96. * adjust buffer index pointer as if we copied data out
  97. * @param n Number of elements we copied from the buffer
  98. * @return Number of elements actually available from the buffer
  99. */
  100. inline size_t consume(size_t n)
  101. {
  102. n = std::min(n, count());
  103. if (n == 0) {
  104. return n;
  105. }
  106. if (wrap) {
  107. wrap = false;
  108. }
  109. const size_t first_chunk = std::min(n, S - begin);
  110. begin = (begin + first_chunk) % S;
  111. if (first_chunk < n) {
  112. const size_t second_chunk = n - first_chunk;
  113. begin = (begin + second_chunk) % S;
  114. }
  115. return n;
  116. }
  117. /**
  118. * @param data Buffer that is to be written to the ring
  119. * @param n Number of elements to write to the buffer
  120. */
  121. inline size_t write(const T * data, size_t n)
  122. {
  123. n = std::min(n, getFree());
  124. if (n == 0) {
  125. return n;
  126. }
  127. const size_t first_chunk = std::min(n, S - end);
  128. memcpy(buf + end, data, first_chunk * sizeof(T));
  129. end = (end + first_chunk) % S;
  130. if (first_chunk < n) {
  131. const size_t second_chunk = n - first_chunk;
  132. memcpy(buf + end, data + first_chunk, second_chunk * sizeof(T));
  133. end = (end + second_chunk) % S;
  134. }
  135. if (begin == end) {
  136. wrap = true;
  137. }
  138. return n;
  139. }
  140. /**
  141. * Place a single value on the buffer. If the buffer is full, consume a value first.
  142. *
  143. * @param value A single value to be placed in the buffer
  144. */
  145. inline void push(const T value)
  146. {
  147. if (count() == S) {
  148. consume(1);
  149. }
  150. const size_t first_chunk = std::min((size_t)1, S - end);
  151. *(buf + end) = value;
  152. end = (end + first_chunk) % S;
  153. if (begin == end) {
  154. wrap = true;
  155. }
  156. }
  157. /**
  158. * @return The most recently pushed element on the buffer
  159. */
  160. inline T get_most_recent() { return *(buf + end); }
  161. /**
  162. * @param dest Destination buffer
  163. * @param n Size (in terms of number of elements) of the destination buffer
  164. * @return Number of elements read from the buffer
  165. */
  166. inline size_t read(T *dest,size_t n)
  167. {
  168. n = std::min(n, count());
  169. if (n == 0) {
  170. return n;
  171. }
  172. if (wrap) {
  173. wrap = false;
  174. }
  175. const size_t first_chunk = std::min(n, S - begin);
  176. memcpy(dest, buf + begin, first_chunk * sizeof(T));
  177. begin = (begin + first_chunk) % S;
  178. if (first_chunk < n) {
  179. const size_t second_chunk = n - first_chunk;
  180. memcpy(dest + first_chunk, buf + begin, second_chunk * sizeof(T));
  181. begin = (begin + second_chunk) % S;
  182. }
  183. return n;
  184. }
  185. /**
  186. * Return how many elements are in the buffer, O(1).
  187. *
  188. * @return The number of elements in the buffer
  189. */
  190. inline size_t count()
  191. {
  192. if (end == begin) {
  193. return wrap ? S : 0;
  194. }
  195. else if (end > begin) {
  196. return end - begin;
  197. }
  198. else {
  199. return S + end - begin;
  200. }
  201. }
  202. /**
  203. * @return The number of slots that are unused in the buffer
  204. */
  205. inline size_t getFree() { return S - count(); }
  206. /**
  207. * @return The arithmetic mean of the contents of the buffer
  208. */
  209. inline float mean()
  210. {
  211. size_t iterator = begin;
  212. float subtotal = 0;
  213. size_t curr_cnt = count();
  214. for (size_t i=0; i<curr_cnt; i++) {
  215. iterator = (iterator + S - 1) % curr_cnt;
  216. subtotal += (float)*(buf + iterator);
  217. }
  218. return curr_cnt ? subtotal / (float)curr_cnt : 0;
  219. }
  220. /**
  221. * @return The arithmetic mean of the most recent 'n' elements of the buffer
  222. */
  223. inline float mean(size_t n)
  224. {
  225. n = n < S ? n : S;
  226. size_t iterator = begin;
  227. float subtotal = 0;
  228. size_t curr_cnt = count();
  229. for (size_t i=0; i<n; i++) {
  230. iterator = (iterator + S - 1) % curr_cnt;
  231. subtotal += (float)*(buf + iterator);
  232. }
  233. return curr_cnt ? subtotal / (float)curr_cnt : 0;
  234. }
  235. /**
  236. * @return The sample standard deviation of element values
  237. */
  238. inline float stddev() { return sqrt(variance()); }
  239. /**
  240. * @return The variance of element values
  241. */
  242. inline float variance()
  243. {
  244. size_t iterator = begin;
  245. float cached_mean = mean();
  246. size_t curr_cnt = count();
  247. T sum_of_squared_deviations = 0;
  248. for (size_t i=0; i<curr_cnt; i++) {
  249. iterator = (iterator + S - 1) % curr_cnt;
  250. float deviation = (buf[i] - cached_mean);
  251. sum_of_squared_deviations += (T)(deviation*deviation);
  252. }
  253. float variance = (float)sum_of_squared_deviations / (float)(S - 1);
  254. return variance;
  255. }
  256. /**
  257. * @return The number of elements of zero value
  258. */
  259. inline size_t zeroCount()
  260. {
  261. size_t iterator = begin;
  262. size_t zeros = 0;
  263. size_t curr_cnt = count();
  264. for (size_t i=0; i<curr_cnt; i++) {
  265. iterator = (iterator + S - 1) % curr_cnt;
  266. if (*(buf + iterator) == 0) {
  267. zeros++;
  268. }
  269. }
  270. return zeros;
  271. }
  272. /**
  273. * @param value Value to match against in buffer
  274. * @return The number of values held in the ring buffer which match a given value
  275. */
  276. inline size_t countValue(T value)
  277. {
  278. size_t iterator = begin;
  279. size_t cnt = 0;
  280. size_t curr_cnt = count();
  281. for (size_t i=0; i<curr_cnt; i++) {
  282. iterator = (iterator + S - 1) % curr_cnt;
  283. if (*(buf + iterator) == value) {
  284. cnt++;
  285. }
  286. }
  287. return cnt;
  288. }
  289. /**
  290. * Print the contents of the buffer
  291. */
  292. /*
  293. inline void dump()
  294. {
  295. size_t iterator = begin;
  296. for (size_t i=0; i<S; i++) {
  297. iterator = (iterator + S - 1) % S;
  298. if (typeid(T) == typeid(int)) {
  299. //DEBUG_INFO("buf[%2zu]=%2d", iterator, (int)*(buf + iterator));
  300. }
  301. else {
  302. //DEBUG_INFO("buf[%2zu]=%2f", iterator, (float)*(buf + iterator));
  303. }
  304. }
  305. }
  306. */
  307. };
  308. } // namespace ZeroTier
  309. #endif