From 77b1d497a1addf6bd82110f84d5b8315ad5534b0 Mon Sep 17 00:00:00 2001 From: Dustin Campbell Date: Thu, 9 Feb 2023 05:30:14 -0800 Subject: [PATCH] Use more efficient stack for FlattenSpansInReverse --- ....ChildSyntaxListReversedEnumeratorStack.cs | 107 ++++++++++++++++++ .../LegacySyntaxNodeExtensions.NodeStack.cs | 51 --------- .../src/Legacy/LegacySyntaxNodeExtensions.cs | 15 +-- 3 files changed, 113 insertions(+), 60 deletions(-) create mode 100644 src/Compiler/Microsoft.AspNetCore.Razor.Language/src/Legacy/LegacySyntaxNodeExtensions.ChildSyntaxListReversedEnumeratorStack.cs delete mode 100644 src/Compiler/Microsoft.AspNetCore.Razor.Language/src/Legacy/LegacySyntaxNodeExtensions.NodeStack.cs diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/src/Legacy/LegacySyntaxNodeExtensions.ChildSyntaxListReversedEnumeratorStack.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/src/Legacy/LegacySyntaxNodeExtensions.ChildSyntaxListReversedEnumeratorStack.cs new file mode 100644 index 00000000000..ecf2c83fab8 --- /dev/null +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/src/Legacy/LegacySyntaxNodeExtensions.ChildSyntaxListReversedEnumeratorStack.cs @@ -0,0 +1,107 @@ +// 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; + +namespace Microsoft.AspNetCore.Razor.Language.Legacy; + +internal static partial class LegacySyntaxNodeExtensions +{ + /// + /// This is similar to . + /// 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. + /// + private struct ChildSyntaxListReversedEnumeratorStack : IDisposable + { + private const int MaxArraySize = 256; + + private static readonly ObjectPool s_stackPool = new(() => new ChildSyntaxList.Reversed.Enumerator[16]); + + private ChildSyntaxList.Reversed.Enumerator[] _stack; + private int _stackPtr; + + public ChildSyntaxListReversedEnumeratorStack(SyntaxNode node) + { + _stack = s_stackPool.Allocate(); + _stackPtr = -1; + + PushRightmostChildren(node); + } + + private void PushRightmostChildren(SyntaxNode node) + { + var current = node; + do + { + var children = current.ChildNodes(); + if (children.Count == 0) + { + break; + } + + if (++_stackPtr == _stack.Length) + { + Array.Resize(ref _stack, _stack.Length * 2); + } + + _stack[_stackPtr] = children.Reverse().GetEnumerator(); + + current = children.Last(); + } + while (current is not null); + } + + private bool TryMoveNextAndGetCurrent([NotNullWhen(true)] out SyntaxNode? node) + { + if (_stackPtr < 0) + { + node = null; + return false; + } + + ref var enumerator = ref _stack[_stackPtr]; + + if (!enumerator.MoveNext()) + { + node = null; + return false; + } + + node = enumerator.Current; + return true; + } + + public bool TryGetNextNode([NotNullWhen(true)] out SyntaxNode? node) + { + while (!TryMoveNextAndGetCurrent(out node)) + { + _stackPtr--; + + if (_stackPtr < 0) + { + node = null; + return false; + } + } + + PushRightmostChildren(node); + return true; + } + + public bool IsEmpty + => _stackPtr < 0; + + public void Dispose() + { + // Return only reasonably-sized stacks to the pool. + if (_stack.Length < MaxArraySize) + { + Array.Clear(_stack, 0, _stack.Length); + s_stackPool.Free(_stack); + } + } + } +} diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/src/Legacy/LegacySyntaxNodeExtensions.NodeStack.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/src/Legacy/LegacySyntaxNodeExtensions.NodeStack.cs deleted file mode 100644 index 6fbc5550342..00000000000 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/src/Legacy/LegacySyntaxNodeExtensions.NodeStack.cs +++ /dev/null @@ -1,51 +0,0 @@ -// 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.Collections.Generic; -using Microsoft.AspNetCore.Razor.Language.Syntax; - -namespace Microsoft.AspNetCore.Razor.Language.Legacy; - -internal static partial class LegacySyntaxNodeExtensions -{ - private struct NodeStack : IDisposable - { - private static readonly ObjectPool s_stackPool = new(() => new SyntaxNode[64]); - - private readonly SyntaxNode[] _stack; - private int _stackPtr; - - public NodeStack(IEnumerable nodes) - { - _stack = s_stackPool.Allocate(); - _stackPtr = -1; - - foreach (var node in nodes) - { - if (++_stackPtr == _stack.Length) - { - Array.Resize(ref _stack, _stack.Length * 2); - } - - _stack[_stackPtr] = node; - } - } - - public SyntaxNode Pop() - => _stack[_stackPtr--]; - - public bool IsEmpty - => _stackPtr < 0; - - public void Dispose() - { - // Return only reasonably-sized stacks to the pool. - if (_stack.Length < 256) - { - Array.Clear(_stack, 0, _stack.Length); - s_stackPool.Free(_stack); - } - } - } -} diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/src/Legacy/LegacySyntaxNodeExtensions.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/src/Legacy/LegacySyntaxNodeExtensions.cs index fc2340ae666..24118ff0f70 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/src/Legacy/LegacySyntaxNodeExtensions.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/src/Legacy/LegacySyntaxNodeExtensions.cs @@ -242,14 +242,11 @@ public static bool IsSpanKind(this SyntaxNode node) private static IEnumerable FlattenSpansInReverse(this SyntaxNode node) { - using var stack = new NodeStack(node.DescendantNodes()); + using var stack = new ChildSyntaxListReversedEnumeratorStack(node); - // Iterate through stack. - while (!stack.IsEmpty) + while (stack.TryGetNextNode(out var nextNode)) { - var child = stack.Pop(); - - if (child is MarkupStartTagSyntax startTag) + if (nextNode is MarkupStartTagSyntax startTag) { var children = startTag.Children; @@ -262,7 +259,7 @@ private static IEnumerable FlattenSpansInReverse(this SyntaxNode nod } } } - else if (child is MarkupEndTagSyntax endTag) + else if (nextNode is MarkupEndTagSyntax endTag) { var children = endTag.Children; @@ -275,9 +272,9 @@ private static IEnumerable FlattenSpansInReverse(this SyntaxNode nod } } } - else if (child.IsSpanKind()) + else if (nextNode.IsSpanKind()) { - yield return child; + yield return nextNode; } } }