using System.Collections; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using Jint.Runtime; namespace Jint.Collections; /// /// Stack for struct types. /// internal sealed class RefStack : IEnumerable where T : struct { internal T[] _array; internal int _size; private const int DefaultCapacity = 2; public RefStack(int capacity = DefaultCapacity) { _array = new T[capacity]; _size = 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref readonly T Peek() { return ref _array[_size - 1]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref readonly T Peek(int fromTop) { var index = _size - 1 - fromTop; return ref _array[index]; } public T this[int index] => _array[index]; [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryPeek([NotNullWhen(true)] out T item) { if (_size > 0) { item = _array[_size - 1]; return true; } item = default; return false; } /// /// Returns the last added item from the stack and decreases the stack size by one. /// /// Is thrown if the stack is empty. [MethodImpl(MethodImplOptions.AggressiveInlining)] public T Pop() { if (_size == 0) { Throw.InvalidOperationException("stack is empty"); } _size--; // Create a copy of the value because it will get overridden // in the statement. var result = _array[_size]; // Ensure that the item can get garbage collected if possible. _array[_size] = default; return result; } /// /// Same as but without copying the value. /// /// /// Use this method when the return value is not used and /// is a value type. In that case, this methods /// avoids one value type copy compared to . /// /// Is thrown if the stack is empty. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PopAndDiscard() { if (_size == 0) { Throw.InvalidOperationException("stack is empty"); } _size--; // Ensure that the item can get garbage collected if possible. _array[_size] = default; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Push(in T item) { if (_size == _array.Length) { EnsureCapacity(_size + 1); } _array[_size++] = item; } private void EnsureCapacity(int min) { var array = _array; if (array.Length < min) { var newCapacity = array.Length == 0 ? DefaultCapacity : array.Length * 2; if (newCapacity < min) { newCapacity = min; } Resize(newCapacity); } } private void Resize(int value) { if (value != _array.Length) { if (value > 0) { var newItems = new T[value]; if (_size > 0) { Array.Copy(_array, 0, newItems, 0, _size); } _array = newItems; } else { _array = []; } } } public void Clear() { if (_size > 0) { #if NETFRAMEWORK || NETSTANDARD2_0 for (var i = 0; i < _size; i++) { _array[i] = default; } #else Array.Fill(_array, default, 0, _size); #endif _size = 0; } } public Enumerator GetEnumerator() { return new Enumerator(this); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } internal struct Enumerator : IEnumerator { private readonly RefStack _stack; private int _index; private T? _currentElement; internal Enumerator(RefStack stack) { _stack = stack; _index = -2; _currentElement = default; } public void Dispose() { _index = -1; } public bool MoveNext() { bool returnValue; if (_index == -2) { // First call to enumerator. _index = _stack._size - 1; returnValue = (_index >= 0); if (returnValue) { _currentElement = _stack._array[_index]; } return returnValue; } if (_index == -1) { // End of enumeration. return false; } returnValue = (--_index >= 0); if (returnValue) { _currentElement = _stack._array[_index]; } else { _currentElement = default; } return returnValue; } public T Current => (T) _currentElement!; object? IEnumerator.Current => Current; void IEnumerator.Reset() { _index = -2; _currentElement = default; } } }