RingBuffer.cs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. #region License
  2. /* Copyright 2015 Joe Osborne
  3. *
  4. * This file is part of RingBuffer.
  5. *
  6. * RingBuffer is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * RingBuffer is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with RingBuffer. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #endregion
  20. using System;
  21. using System.Collections;
  22. using System.Collections.Generic;
  23. namespace RingBuffer {
  24. /// <summary>
  25. /// A generic ring buffer with fixed capacity.
  26. /// </summary>
  27. /// <typeparam name="T">The type of data stored in the buffer</typeparam>
  28. public class RingBuffer<T> : IEnumerable<T>, IEnumerable, ICollection<T>,
  29. ICollection {
  30. protected int head = 0;
  31. protected int tail = 0;
  32. protected int size = 0;
  33. protected T[] buffer;
  34. private bool allowOverflow;
  35. public bool AllowOverflow { get { return allowOverflow; } }
  36. /// <summary>
  37. /// The total number of elements the buffer can store (grows).
  38. /// </summary>
  39. public int Capacity { get { return buffer.Length; } }
  40. /// <summary>
  41. /// The number of elements currently contained in the buffer.
  42. /// </summary>
  43. public int Size { get { return size; } }
  44. /// <summary>
  45. /// Retrieve the next item from the buffer.
  46. /// </summary>
  47. /// <returns>The oldest item added to the buffer.</returns>
  48. public T Get() {
  49. if(size == 0) throw new System.InvalidOperationException("Buffer is empty.");
  50. T _item = buffer[head];
  51. head = (head + 1) % Capacity;
  52. size--;
  53. return _item;
  54. }
  55. /// <summary>
  56. /// Adds an item to the end of the buffer.
  57. /// </summary>
  58. /// <param name="item">The item to be added.</param>
  59. public void Put(T item) {
  60. // If tail & head are equal and the buffer is not empty, assume
  61. // that it will overflow and throw an exception.
  62. if(tail == head && size != 0) {
  63. if(allowOverflow) {
  64. addToBuffer(item, true);
  65. }
  66. else {
  67. throw new System.InvalidOperationException("The RingBuffer is full");
  68. }
  69. }
  70. // If the buffer will not overflow, just add the item.
  71. else {
  72. addToBuffer(item, false);
  73. }
  74. }
  75. public void Put(T[] item)
  76. {
  77. foreach (var t in item)
  78. {
  79. Put(t);
  80. }
  81. }
  82. protected void addToBuffer(T toAdd, bool overflow) {
  83. if(overflow) {
  84. head = (head + 1) % Capacity;
  85. }
  86. else {
  87. size++;
  88. }
  89. buffer[tail] = toAdd;
  90. tail = (tail + 1) % Capacity;
  91. }
  92. #region Constructors
  93. // Default capacity is 4, default overflow behavior is false.
  94. public RingBuffer() : this(4) { }
  95. public RingBuffer(int capacity) : this(capacity, false) { }
  96. public RingBuffer(int capacity, bool overflow) {
  97. buffer = new T[capacity];
  98. allowOverflow = overflow;
  99. }
  100. #endregion
  101. #region IEnumerable Members
  102. public IEnumerator<T> GetEnumerator() {
  103. int _index = head;
  104. for(int i = 0; i < size; i++, _index = (_index + 1) % Capacity) {
  105. yield return buffer[_index];
  106. }
  107. }
  108. IEnumerator<T> IEnumerable<T>.GetEnumerator() {
  109. return GetEnumerator();
  110. }
  111. IEnumerator IEnumerable.GetEnumerator() {
  112. return (IEnumerator)GetEnumerator();
  113. }
  114. #endregion
  115. #region ICollection<T> Members
  116. public int Count { get { return size; } }
  117. public bool IsReadOnly { get { return false; } }
  118. public void Add(T item) {
  119. Put(item);
  120. }
  121. /// <summary>
  122. /// Determines whether the RingBuffer contains a specific value.
  123. /// </summary>
  124. /// <param name="item">The value to check the RingBuffer for.</param>
  125. /// <returns>True if the RingBuffer contains <paramref name="item"/>
  126. /// , false if it does not.
  127. /// </returns>
  128. public bool Contains(T item) {
  129. EqualityComparer<T> comparer = EqualityComparer<T>.Default;
  130. int _index = head;
  131. for(int i = 0; i < size; i++, _index = (_index + 1) % Capacity) {
  132. if(comparer.Equals(item, buffer[_index])) return true;
  133. }
  134. return false;
  135. }
  136. /// <summary>
  137. /// Removes all items from the RingBuffer.
  138. /// </summary>
  139. public void Clear() {
  140. for(int i = 0; i < Capacity; i++) {
  141. buffer[i] = default(T);
  142. }
  143. head = 0;
  144. tail = 0;
  145. size = 0;
  146. }
  147. /// <summary>
  148. /// Copies the contents of the RingBuffer to <paramref name="array"/>
  149. /// starting at <paramref name="arrayIndex"/>.
  150. /// </summary>
  151. /// <param name="array">The array to be copied to.</param>
  152. /// <param name="arrayIndex">The index of <paramref name="array"/>
  153. /// where the buffer should begin copying to.</param>
  154. public void CopyTo(T[] array, int arrayIndex) {
  155. int _index = head;
  156. for(int i = 0; i < size; i++, arrayIndex++, _index = (_index + 1) %
  157. Capacity) {
  158. array[arrayIndex] = buffer[_index];
  159. }
  160. }
  161. /// <summary>
  162. /// Removes <paramref name="item"/> from the buffer.
  163. /// </summary>
  164. /// <param name="item"></param>
  165. /// <returns>True if <paramref name="item"/> was found and
  166. /// successfully removed. False if <paramref name="item"/> was not
  167. /// found or there was a problem removing it from the RingBuffer.
  168. /// </returns>
  169. public bool Remove(T item) {
  170. int _index = head;
  171. int _removeIndex = 0;
  172. bool _foundItem = false;
  173. EqualityComparer<T> _comparer = EqualityComparer<T>.Default;
  174. for(int i = 0; i < size; i++, _index = (_index + 1) % Capacity) {
  175. if(_comparer.Equals(item, buffer[_index])) {
  176. _removeIndex = _index;
  177. _foundItem = true;
  178. break;
  179. }
  180. }
  181. if(_foundItem) {
  182. T[] _newBuffer = new T[size - 1];
  183. _index = head;
  184. bool _pastItem = false;
  185. for(int i = 0; i < size - 1; i++, _index = (_index + 1) % Capacity) {
  186. if(_index == _removeIndex) {
  187. _pastItem = true;
  188. }
  189. if(_pastItem) {
  190. _newBuffer[_index] = buffer[(_index + 1) % Capacity];
  191. }
  192. else {
  193. _newBuffer[_index] = buffer[_index];
  194. }
  195. }
  196. size--;
  197. buffer = _newBuffer;
  198. return true;
  199. }
  200. return false;
  201. }
  202. #endregion
  203. #region ICollection Members
  204. /// <summary>
  205. /// Gets an object that can be used to synchronize access to the
  206. /// RingBuffer.
  207. /// </summary>
  208. public Object SyncRoot { get { return this; } }
  209. /// <summary>
  210. /// Gets a value indicating whether access to the RingBuffer is
  211. /// synchronized (thread safe).
  212. /// </summary>
  213. public bool IsSynchronized { get { return false; } }
  214. /// <summary>
  215. /// Copies the elements of the RingBuffer to <paramref name="array"/>,
  216. /// starting at a particular Array <paramref name="index"/>.
  217. /// </summary>
  218. /// <param name="array">The one-dimensional Array that is the
  219. /// destination of the elements copied from RingBuffer. The Array must
  220. /// have zero-based indexing.</param>
  221. /// <param name="index">The zero-based index in
  222. /// <paramref name="array"/> at which copying begins.</param>
  223. void ICollection.CopyTo(Array array, int index) {
  224. CopyTo((T[])array, index);
  225. }
  226. #endregion
  227. }
  228. }