CircularBuffer.cs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. using System;
  2. using UnityEngine;
  3. namespace IngameDebugConsole
  4. {
  5. public class CircularBuffer<T>
  6. {
  7. private readonly T[] array;
  8. private int startIndex;
  9. public int Count { get; private set; }
  10. public T this[int index] { get { return array[( startIndex + index ) % array.Length]; } }
  11. public CircularBuffer( int capacity )
  12. {
  13. array = new T[capacity];
  14. }
  15. // Old elements are overwritten when capacity is reached
  16. public void Add( T value )
  17. {
  18. if( Count < array.Length )
  19. array[Count++] = value;
  20. else
  21. {
  22. array[startIndex] = value;
  23. if( ++startIndex >= array.Length )
  24. startIndex = 0;
  25. }
  26. }
  27. public T[] ToArray()
  28. {
  29. T[] result = new T[Count];
  30. for (int i = 0; i < Count; i++)
  31. result[i] = this[i];
  32. return result;
  33. }
  34. }
  35. public class DynamicCircularBuffer<T>
  36. {
  37. private T[] array;
  38. private int startIndex;
  39. public int Count { get; private set; }
  40. public int Capacity { get { return array.Length; } }
  41. public T this[int index]
  42. {
  43. get { return array[( startIndex + index ) % array.Length]; }
  44. set { array[( startIndex + index ) % array.Length] = value; }
  45. }
  46. public DynamicCircularBuffer( int initialCapacity = 2 )
  47. {
  48. array = new T[initialCapacity];
  49. }
  50. private void SetCapacity( int capacity )
  51. {
  52. T[] newArray = new T[capacity];
  53. if( Count > 0 )
  54. {
  55. int elementsBeforeWrap = Mathf.Min( Count, array.Length - startIndex );
  56. Array.Copy( array, startIndex, newArray, 0, elementsBeforeWrap );
  57. if( elementsBeforeWrap < Count )
  58. Array.Copy( array, 0, newArray, elementsBeforeWrap, Count - elementsBeforeWrap );
  59. }
  60. array = newArray;
  61. startIndex = 0;
  62. }
  63. /// <summary>Inserts the value to the beginning of the collection.</summary>
  64. public void AddFirst( T value )
  65. {
  66. if( array.Length == Count )
  67. SetCapacity( Mathf.Max( array.Length * 2, 4 ) );
  68. startIndex = ( startIndex > 0 ) ? ( startIndex - 1 ) : ( array.Length - 1 );
  69. array[startIndex] = value;
  70. Count++;
  71. }
  72. /// <summary>Adds the value to the end of the collection.</summary>
  73. public void Add( T value )
  74. {
  75. if( array.Length == Count )
  76. SetCapacity( Mathf.Max( array.Length * 2, 4 ) );
  77. this[Count++] = value;
  78. }
  79. public void AddRange( DynamicCircularBuffer<T> other )
  80. {
  81. if( other.Count == 0 )
  82. return;
  83. if( array.Length < Count + other.Count )
  84. SetCapacity( Mathf.Max( array.Length * 2, Count + other.Count ) );
  85. int insertStartIndex = ( startIndex + Count ) % array.Length;
  86. int elementsBeforeWrap = Mathf.Min( other.Count, array.Length - insertStartIndex );
  87. int otherElementsBeforeWrap = Mathf.Min( other.Count, other.array.Length - other.startIndex );
  88. Array.Copy( other.array, other.startIndex, array, insertStartIndex, Mathf.Min( elementsBeforeWrap, otherElementsBeforeWrap ) );
  89. if( elementsBeforeWrap < otherElementsBeforeWrap ) // This array wrapped before the other array
  90. Array.Copy( other.array, other.startIndex + elementsBeforeWrap, array, 0, otherElementsBeforeWrap - elementsBeforeWrap );
  91. else if( elementsBeforeWrap > otherElementsBeforeWrap ) // The other array wrapped before this array
  92. Array.Copy( other.array, 0, array, insertStartIndex + otherElementsBeforeWrap, elementsBeforeWrap - otherElementsBeforeWrap );
  93. int copiedElements = Mathf.Max( elementsBeforeWrap, otherElementsBeforeWrap );
  94. if( copiedElements < other.Count ) // Both arrays wrapped and there's still some elements left to copy
  95. Array.Copy( other.array, copiedElements - otherElementsBeforeWrap, array, copiedElements - elementsBeforeWrap, other.Count - copiedElements );
  96. Count += other.Count;
  97. }
  98. public T RemoveFirst()
  99. {
  100. T element = array[startIndex];
  101. array[startIndex] = default( T );
  102. if( ++startIndex == array.Length )
  103. startIndex = 0;
  104. Count--;
  105. return element;
  106. }
  107. public T RemoveLast()
  108. {
  109. int index = ( startIndex + Count - 1 ) % array.Length;
  110. T element = array[index];
  111. array[index] = default( T );
  112. Count--;
  113. return element;
  114. }
  115. public int RemoveAll( Predicate<T> shouldRemoveElement )
  116. {
  117. return RemoveAll<T>( shouldRemoveElement, null, null );
  118. }
  119. public int RemoveAll<Y>( Predicate<T> shouldRemoveElement, Action<T, int> onElementIndexChanged, DynamicCircularBuffer<Y> synchronizedBuffer )
  120. {
  121. Y[] synchronizedArray = ( synchronizedBuffer != null ) ? synchronizedBuffer.array : null;
  122. int elementsBeforeWrap = Mathf.Min( Count, array.Length - startIndex );
  123. int removedElements = 0;
  124. int i = startIndex, newIndex = startIndex, endIndex = startIndex + elementsBeforeWrap;
  125. for( ; i < endIndex; i++ )
  126. {
  127. if( shouldRemoveElement( array[i] ) )
  128. removedElements++;
  129. else
  130. {
  131. if( removedElements > 0 )
  132. {
  133. T element = array[i];
  134. array[newIndex] = element;
  135. if( synchronizedArray != null )
  136. synchronizedArray[newIndex] = synchronizedArray[i];
  137. if( onElementIndexChanged != null )
  138. onElementIndexChanged( element, newIndex - startIndex );
  139. }
  140. newIndex++;
  141. }
  142. }
  143. i = 0;
  144. endIndex = Count - elementsBeforeWrap;
  145. if( newIndex < array.Length )
  146. {
  147. for( ; i < endIndex; i++ )
  148. {
  149. if( shouldRemoveElement( array[i] ) )
  150. removedElements++;
  151. else
  152. {
  153. T element = array[i];
  154. array[newIndex] = element;
  155. if( synchronizedArray != null )
  156. synchronizedArray[newIndex] = synchronizedArray[i];
  157. if( onElementIndexChanged != null )
  158. onElementIndexChanged( element, newIndex - startIndex );
  159. if( ++newIndex == array.Length )
  160. {
  161. i++;
  162. break;
  163. }
  164. }
  165. }
  166. }
  167. if( newIndex == array.Length )
  168. {
  169. newIndex = 0;
  170. for( ; i < endIndex; i++ )
  171. {
  172. if( shouldRemoveElement( array[i] ) )
  173. removedElements++;
  174. else
  175. {
  176. if( removedElements > 0 )
  177. {
  178. T element = array[i];
  179. array[newIndex] = element;
  180. if( synchronizedArray != null )
  181. synchronizedArray[newIndex] = synchronizedArray[i];
  182. if( onElementIndexChanged != null )
  183. onElementIndexChanged( element, newIndex + elementsBeforeWrap );
  184. }
  185. newIndex++;
  186. }
  187. }
  188. }
  189. TrimEnd( removedElements );
  190. if( synchronizedBuffer != null )
  191. synchronizedBuffer.TrimEnd( removedElements );
  192. return removedElements;
  193. }
  194. public void TrimStart( int trimCount, Action<T> perElementCallback = null )
  195. {
  196. TrimInternal( trimCount, startIndex, perElementCallback );
  197. startIndex = ( startIndex + trimCount ) % array.Length;
  198. }
  199. public void TrimEnd( int trimCount, Action<T> perElementCallback = null )
  200. {
  201. TrimInternal( trimCount, ( startIndex + Count - trimCount ) % array.Length, perElementCallback );
  202. }
  203. private void TrimInternal( int trimCount, int startIndex, Action<T> perElementCallback )
  204. {
  205. int elementsBeforeWrap = Mathf.Min( trimCount, array.Length - startIndex );
  206. if( perElementCallback == null )
  207. {
  208. Array.Clear( array, startIndex, elementsBeforeWrap );
  209. if( elementsBeforeWrap < trimCount )
  210. Array.Clear( array, 0, trimCount - elementsBeforeWrap );
  211. }
  212. else
  213. {
  214. for( int i = startIndex, endIndex = startIndex + elementsBeforeWrap; i < endIndex; i++ )
  215. {
  216. perElementCallback( array[i] );
  217. array[i] = default( T );
  218. }
  219. for( int i = 0, endIndex = trimCount - elementsBeforeWrap; i < endIndex; i++ )
  220. {
  221. perElementCallback( array[i] );
  222. array[i] = default( T );
  223. }
  224. }
  225. Count -= trimCount;
  226. }
  227. public void Clear()
  228. {
  229. int elementsBeforeWrap = Mathf.Min( Count, array.Length - startIndex );
  230. Array.Clear( array, startIndex, elementsBeforeWrap );
  231. if( elementsBeforeWrap < Count )
  232. Array.Clear( array, 0, Count - elementsBeforeWrap );
  233. startIndex = 0;
  234. Count = 0;
  235. }
  236. public int IndexOf( T value )
  237. {
  238. int elementsBeforeWrap = Mathf.Min( Count, array.Length - startIndex );
  239. int index = Array.IndexOf( array, value, startIndex, elementsBeforeWrap );
  240. if( index >= 0 )
  241. return index - startIndex;
  242. if( elementsBeforeWrap < Count )
  243. {
  244. index = Array.IndexOf( array, value, 0, Count - elementsBeforeWrap );
  245. if( index >= 0 )
  246. return index + elementsBeforeWrap;
  247. }
  248. return -1;
  249. }
  250. public void ForEach( Action<T> action )
  251. {
  252. int elementsBeforeWrap = Mathf.Min( Count, array.Length - startIndex );
  253. for( int i = startIndex, endIndex = startIndex + elementsBeforeWrap; i < endIndex; i++ )
  254. action( array[i] );
  255. for( int i = 0, endIndex = Count - elementsBeforeWrap; i < endIndex; i++ )
  256. action( array[i] );
  257. }
  258. }
  259. }