RefStack.cs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. using System.Collections;
  2. using System.Diagnostics.CodeAnalysis;
  3. using System.Runtime.CompilerServices;
  4. using Jint.Runtime;
  5. namespace Jint.Collections;
  6. /// <summary>
  7. /// Stack for struct types.
  8. /// </summary>
  9. internal sealed class RefStack<T> : IEnumerable<T> where T : struct
  10. {
  11. internal T[] _array;
  12. internal int _size;
  13. private const int DefaultCapacity = 2;
  14. public RefStack(int capacity = DefaultCapacity)
  15. {
  16. _array = new T[capacity];
  17. _size = 0;
  18. }
  19. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  20. public ref readonly T Peek()
  21. {
  22. return ref _array[_size - 1];
  23. }
  24. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  25. public ref readonly T Peek(int fromTop)
  26. {
  27. var index = _size - 1 - fromTop;
  28. return ref _array[index];
  29. }
  30. public T this[int index] => _array[index];
  31. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  32. public bool TryPeek([NotNullWhen(true)] out T item)
  33. {
  34. if (_size > 0)
  35. {
  36. item = _array[_size - 1];
  37. return true;
  38. }
  39. item = default;
  40. return false;
  41. }
  42. /// <summary>
  43. /// Returns the last added item from the stack and decreases the stack size by one.
  44. /// </summary>
  45. /// <exception cref="InvalidOperationException">Is thrown if the stack is empty.</exception>
  46. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  47. public T Pop()
  48. {
  49. if (_size == 0)
  50. {
  51. Throw.InvalidOperationException("stack is empty");
  52. }
  53. _size--;
  54. // Create a copy of the value because it will get overridden
  55. // in the statement.
  56. var result = _array[_size];
  57. // Ensure that the item can get garbage collected if possible.
  58. _array[_size] = default;
  59. return result;
  60. }
  61. /// <summary>
  62. /// Same as <see cref="Pop"/> but without copying the value.
  63. /// </summary>
  64. /// <remarks>
  65. /// Use this method when the return value is not used and
  66. /// <typeparamref name="T"/> is a value type. In that case, this methods
  67. /// avoids one value type copy compared to <see cref="Pop"/>.
  68. /// </remarks>
  69. /// <exception cref="InvalidOperationException">Is thrown if the stack is empty.</exception>
  70. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  71. public void PopAndDiscard()
  72. {
  73. if (_size == 0)
  74. {
  75. Throw.InvalidOperationException("stack is empty");
  76. }
  77. _size--;
  78. // Ensure that the item can get garbage collected if possible.
  79. _array[_size] = default;
  80. }
  81. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  82. public void Push(in T item)
  83. {
  84. if (_size == _array.Length)
  85. {
  86. EnsureCapacity(_size + 1);
  87. }
  88. _array[_size++] = item;
  89. }
  90. private void EnsureCapacity(int min)
  91. {
  92. var array = _array;
  93. if (array.Length < min)
  94. {
  95. var newCapacity = array.Length == 0
  96. ? DefaultCapacity
  97. : array.Length * 2;
  98. if (newCapacity < min)
  99. {
  100. newCapacity = min;
  101. }
  102. Resize(newCapacity);
  103. }
  104. }
  105. private void Resize(int value)
  106. {
  107. if (value != _array.Length)
  108. {
  109. if (value > 0)
  110. {
  111. var newItems = new T[value];
  112. if (_size > 0)
  113. {
  114. Array.Copy(_array, 0, newItems, 0, _size);
  115. }
  116. _array = newItems;
  117. }
  118. else
  119. {
  120. _array = [];
  121. }
  122. }
  123. }
  124. public void Clear()
  125. {
  126. if (_size > 0)
  127. {
  128. #if NETFRAMEWORK || NETSTANDARD2_0
  129. for (var i = 0; i < _size; i++)
  130. {
  131. _array[i] = default;
  132. }
  133. #else
  134. Array.Fill(_array, default, 0, _size);
  135. #endif
  136. _size = 0;
  137. }
  138. }
  139. public Enumerator GetEnumerator()
  140. {
  141. return new Enumerator(this);
  142. }
  143. IEnumerator<T> IEnumerable<T>.GetEnumerator()
  144. {
  145. return GetEnumerator();
  146. }
  147. IEnumerator IEnumerable.GetEnumerator()
  148. {
  149. return GetEnumerator();
  150. }
  151. internal struct Enumerator : IEnumerator<T>
  152. {
  153. private readonly RefStack<T> _stack;
  154. private int _index;
  155. private T? _currentElement;
  156. internal Enumerator(RefStack<T> stack)
  157. {
  158. _stack = stack;
  159. _index = -2;
  160. _currentElement = default;
  161. }
  162. public void Dispose()
  163. {
  164. _index = -1;
  165. }
  166. public bool MoveNext()
  167. {
  168. bool returnValue;
  169. if (_index == -2)
  170. {
  171. // First call to enumerator.
  172. _index = _stack._size - 1;
  173. returnValue = (_index >= 0);
  174. if (returnValue)
  175. {
  176. _currentElement = _stack._array[_index];
  177. }
  178. return returnValue;
  179. }
  180. if (_index == -1)
  181. {
  182. // End of enumeration.
  183. return false;
  184. }
  185. returnValue = (--_index >= 0);
  186. if (returnValue)
  187. {
  188. _currentElement = _stack._array[_index];
  189. }
  190. else
  191. {
  192. _currentElement = default;
  193. }
  194. return returnValue;
  195. }
  196. public T Current => (T) _currentElement!;
  197. object? IEnumerator.Current => Current;
  198. void IEnumerator.Reset()
  199. {
  200. _index = -2;
  201. _currentElement = default;
  202. }
  203. }
  204. }