Optimize allocations of List<ISymbol> and related

This commit is contained in:
Ryan Nowak 2016-01-14 16:57:53 -08:00
parent ffdf5d2827
commit 95ea4cc06f
3 changed files with 45 additions and 13 deletions

View File

@ -16,6 +16,7 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
public class Span : SyntaxTreeNode
{
private static readonly int TypeHashCode = typeof(Span).GetHashCode();
private string _content;
private SourceLocation _start;
public Span(SpanBuilder builder)
@ -24,7 +25,7 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
}
public SpanKind Kind { get; protected set; }
public IEnumerable<ISymbol> Symbols { get; protected set; }
public IReadOnlyList<ISymbol> Symbols { get; protected set; }
// Allow test code to re-link spans
public Span Previous { get; protected internal set; }
@ -48,7 +49,25 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
get { return _start; }
}
public string Content { get; private set; }
public string Content
{
get
{
if (_content == null)
{
var builder = new StringBuilder();
for (var i = 0; i < Symbols.Count; i++)
{
var symbol = Symbols[i];
builder.Append(symbol.Content);
}
_content = builder.ToString();
}
return _content;
}
}
public void Change(Action<SpanBuilder> changes)
{
@ -59,19 +78,15 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
public void ReplaceWith(SpanBuilder builder)
{
Debug.Assert(!builder.Symbols.Any() || builder.Symbols.All(s => s != null));
Kind = builder.Kind;
Symbols = builder.Symbols;
EditHandler = builder.EditHandler;
ChunkGenerator = builder.ChunkGenerator ?? SpanChunkGenerator.Null;
_start = builder.Start;
_content = null;
// Since we took references to the values in SpanBuilder, clear its references out
builder.Reset();
// Calculate other properties
Content = Symbols.Aggregate(new StringBuilder(), (sb, sym) => sb.Append(sym.Content), sb => sb.ToString());
}
/// <summary>

View File

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Microsoft.AspNet.Razor.Editor;
using Microsoft.AspNet.Razor.Chunks.Generators;
@ -13,7 +12,7 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
{
public class SpanBuilder
{
private IList<ISymbol> _symbols = new List<ISymbol>();
private List<ISymbol> _symbols;
private SourceLocationTracker _tracker = new SourceLocationTracker();
public SpanBuilder(Span original)
@ -31,11 +30,20 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
}
public SourceLocation Start { get; set; }
public SpanKind Kind { get; set; }
public ReadOnlyCollection<ISymbol> Symbols
public IReadOnlyList<ISymbol> Symbols
{
get { return new ReadOnlyCollection<ISymbol>(_symbols); }
get
{
if (_symbols == null)
{
_symbols = new List<ISymbol>();
}
return _symbols;
}
}
public SpanEditHandler EditHandler { get; set; }
@ -43,7 +51,10 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
public void Reset()
{
_symbols = new List<ISymbol>();
// Need to potentially allocate a new list because Span.ReplaceWith takes ownership
// of the original list.
_symbols = null;
EditHandler = SpanEditHandler.CreateDefault(s => Enumerable.Empty<ISymbol>());
ChunkGenerator = SpanChunkGenerator.Null;
Start = SourceLocation.Zero;
@ -67,6 +78,11 @@ namespace Microsoft.AspNet.Razor.Parser.SyntaxTree
return;
}
if (_symbols == null)
{
_symbols = new List<ISymbol>();
}
if (_symbols.Count == 0)
{
Start = symbol.Start;

View File

@ -647,8 +647,9 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
EnsureTagBlock(tagBlock);
var childSpan = (Span)tagBlock.Children.First();
// We grab the symbol that could be forward slash
var relevantSymbol = (HtmlSymbol)childSpan.Symbols.Take(2).Last();
var relevantSymbol = (HtmlSymbol)childSpan.Symbols[childSpan.Symbols.Count == 1 ? 0 : 1];
return relevantSymbol.Type == HtmlSymbolType.ForwardSlash;
}