File: Language\Legacy\LegacySyntaxNodeExtensions.ChildSyntaxListReversedEnumeratorStack.cs
Web Access
Project: src\src\Razor\src\Compiler\Microsoft.CodeAnalysis.Razor.Compiler\src\Microsoft.CodeAnalysis.Razor.Compiler.csproj (Microsoft.CodeAnalysis.Razor.Compiler)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.Extensions.ObjectPool;
 
namespace Microsoft.AspNetCore.Razor.Language.Legacy;
 
internal static partial class LegacySyntaxNodeExtensions
{
    /// <summary>
    ///  This is similar to <see cref="SyntaxNode.ChildSyntaxListEnumeratorStack"/>.
    ///  However, instead of enumerating descendant nodes in a top-down, left-to-right
    ///  fashion, the process is reversed; it operates right-to-left and bottom-up.
    /// </summary>
    private partial struct ChildSyntaxListReversedEnumeratorStack : IDisposable
    {
        private const int MaxArraySize = 256;
 
        private static readonly ObjectPool<ChildSyntaxList.Reversed.Enumerator[]> s_stackPool = DefaultPool.Create(Policy.Instance);
 
        private ChildSyntaxList.Reversed.Enumerator[] _stack;
        private int _stackPtr;
 
        public ChildSyntaxListReversedEnumeratorStack(SyntaxNode node)
        {
            _stack = s_stackPool.Get();
            _stackPtr = -1;
 
            PushRightmostChildren(node);
        }
 
        private void PushRightmostChildren(SyntaxNodeOrToken nodeOrToken)
        {
            var current = nodeOrToken;
            while (true)
            {
                var children = current.ChildNodesAndTokens();
                if (children.Count == 0)
                {
                    break;
                }
 
                if (++_stackPtr == _stack.Length)
                {
                    Array.Resize(ref _stack, _stack.Length * 2);
                }
 
                _stack[_stackPtr] = children.Reverse().GetEnumerator();
 
                current = children.Last();
            }
        }
 
        private bool TryMoveNextAndGetCurrent(out SyntaxNodeOrToken nodeOrToken)
        {
            if (_stackPtr < 0)
            {
                nodeOrToken = default;
                return false;
            }
 
            ref var enumerator = ref _stack[_stackPtr];
 
            if (!enumerator.MoveNext())
            {
                nodeOrToken = default;
                return false;
            }
 
            nodeOrToken = enumerator.Current;
            return true;
        }
 
        public bool TryGetNextNodeOrToken(out SyntaxNodeOrToken node)
        {
            while (!TryMoveNextAndGetCurrent(out node))
            {
                _stackPtr--;
 
                if (_stackPtr < 0)
                {
                    node = null;
                    return false;
                }
            }
 
            PushRightmostChildren(node);
            return true;
        }
 
        public readonly bool IsEmpty
            => _stackPtr < 0;
 
        public void Dispose()
        {
            s_stackPool.Return(_stack);
        }
    }
}