Merge pull request #22877 from dotnet/dev/toddgrun/MorePerfOptimizations

Dev/toddgrun/more perf optimizations
This commit is contained in:
Todd Grunke 2020-06-12 13:25:31 -07:00 committed by GitHub
commit 073cd0aa40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 92 additions and 77 deletions

View File

@ -554,13 +554,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return Transition(CSharpTokenizerState.Data, EndToken(SyntaxKind.StringLiteral));
}
private StateResult QuotedCharacterLiteral() => QuotedLiteral('\'', SyntaxKind.CharacterLiteral);
private StateResult QuotedCharacterLiteral() => QuotedLiteral('\'', IsEndQuotedCharacterLiteral, SyntaxKind.CharacterLiteral);
private StateResult QuotedStringLiteral() => QuotedLiteral('\"', SyntaxKind.StringLiteral);
private StateResult QuotedStringLiteral() => QuotedLiteral('\"', IsEndQuotedStringLiteral, SyntaxKind.StringLiteral);
private StateResult QuotedLiteral(char quote, SyntaxKind literalType)
private Func<char, bool> IsEndQuotedCharacterLiteral = (c) => c == '\\' || c == '\'' || ParserHelpers.IsNewLine(c);
private Func<char, bool> IsEndQuotedStringLiteral = (c) => c == '\\' || c == '\"' || ParserHelpers.IsNewLine(c);
private StateResult QuotedLiteral(char quote, Func<char, bool> isEndQuotedLiteral, SyntaxKind literalType)
{
TakeUntil(c => c == '\\' || c == quote || ParserHelpers.IsNewLine(c));
TakeUntil(isEndQuotedLiteral);
if (CurrentCharacter == '\\')
{
TakeCurrent(); // Take the '\'

View File

@ -13,7 +13,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private readonly IList<TextLine> _lines;
private readonly string _filePath;
private TextLine _currentLine;
private TextLine _endLine;
public LineTrackingStringBuffer(string content, string filePath)
: this(content.ToCharArray(), filePath)
@ -22,17 +21,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public LineTrackingStringBuffer(char[] content, string filePath)
{
_endLine = new TextLine(0, 0);
_lines = new List<TextLine>() { _endLine };
_lines = new List<TextLine>();
Append(content);
BuildTextLines(content);
_filePath = filePath;
}
public int Length
{
get { return _endLine.End; }
get { return _lines[_lines.Count - 1].End; }
}
public SourceLocation EndLocation
@ -43,7 +41,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public CharacterReference CharAt(int absoluteIndex)
{
var line = FindLine(absoluteIndex);
if (line == null)
if (line.IsDefault)
{
throw new ArgumentOutOfRangeException(nameof(absoluteIndex));
}
@ -51,38 +49,38 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return new CharacterReference(line.Content[idx], new SourceLocation(_filePath, absoluteIndex, line.Index, idx));
}
private void Append(char[] content)
private void BuildTextLines(char[] content)
{
string lineText;
var lineStart = 0;
for (int i = 0; i < content.Length; i++)
{
AppendCore(content[i]);
// \r on it's own: Start a new line, otherwise wait for \n
// Other Newline: Start a new line
if ((content[i] == '\r' && (i + 1 == content.Length || content[i + 1] != '\n')) || (content[i] != '\r' && ParserHelpers.IsNewLine(content[i])))
if (ParserHelpers.IsNewLine(content[i]))
{
PushNewLine();
// \r on it's own: Start a new line, otherwise wait for \n
// Other Newline: Start a new line
if (content[i] == '\r' && i + 1 < content.Length && content[i + 1] == '\n')
{
i++;
}
lineText = new string(content, lineStart, (i - lineStart) + 1); // +1 to include the current char
_lines.Add(new TextLine(lineStart, _lines.Count, lineText));
lineStart = i + 1;
}
}
}
private void PushNewLine()
{
_endLine = new TextLine(_endLine.End, _endLine.Index + 1);
_lines.Add(_endLine);
}
private void AppendCore(char chr)
{
Debug.Assert(_lines.Count > 0);
_lines[_lines.Count - 1].Content.Append(chr);
lineText = new string(content, lineStart, content.Length - lineStart); // no +1 as content.Length points past the last char already
_lines.Add(new TextLine(lineStart, _lines.Count, lineText));
}
private TextLine FindLine(int absoluteIndex)
{
TextLine selected = null;
TextLine selected;
if (_currentLine == null)
if (_currentLine.IsDefault)
{
// Scan from line 0
selected = ScanLines(absoluteIndex, 0, _lines.Count);
@ -104,6 +102,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
selected = ScanLines(absoluteIndex, _currentLine.Index, _lines.Count);
}
}
else
{
selected = default;
}
}
else if (absoluteIndex < _currentLine.Start)
{
@ -122,6 +124,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
selected = ScanLines(absoluteIndex, 0, _currentLine.Index);
}
}
else
{
selected = default;
}
}
else
{
@ -129,7 +135,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
selected = _currentLine;
}
Debug.Assert(selected == null || selected.Contains(absoluteIndex));
Debug.Assert(selected.IsDefault || selected.Contains(absoluteIndex));
_currentLine = selected;
return selected;
}
@ -159,7 +165,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
}
}
return null;
return default;
}
internal struct CharacterReference
@ -175,28 +181,26 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public SourceLocation Location { get; }
}
private class TextLine
private struct TextLine
{
private StringBuilder _content = new StringBuilder();
public TextLine(int start, int index)
public TextLine(int start, int index, string content)
{
Start = start;
Index = index;
Content = content;
}
public StringBuilder Content
{
get { return _content; }
}
public string Content { get; }
public bool IsDefault => Content == null;
public int Length
{
get { return Content.Length; }
}
public int Start { get; set; }
public int Index { get; set; }
public int Start { get; }
public int Index { get; }
public int End
{

View File

@ -16,25 +16,25 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private readonly TokenizerView<TTokenizer> _tokenizer;
private SyntaxListBuilder<SyntaxToken>? _tokenBuilder;
// Following four high traffic methods cached as using method groups would cause allocation on every invocation.
protected static readonly Func<SyntaxToken, bool> IsSpacingToken = (token) =>
{
return token.Kind == SyntaxKind.Whitespace;
};
protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingNewLines = (token) =>
{
return IsSpacingToken(token) || token.Kind == SyntaxKind.NewLine;
};
protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingComments = (token) =>
{
return IsSpacingToken(token) || token.Kind == SyntaxKind.CSharpComment;
};
protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingNewLinesAndComments = (token) =>
{
return IsSpacingTokenIncludingNewLines(token) || token.Kind == SyntaxKind.CSharpComment;
// Following four high traffic methods cached as using method groups would cause allocation on every invocation.
protected static readonly Func<SyntaxToken, bool> IsSpacingToken = (token) =>
{
return token.Kind == SyntaxKind.Whitespace;
};
protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingNewLines = (token) =>
{
return IsSpacingToken(token) || token.Kind == SyntaxKind.NewLine;
};
protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingComments = (token) =>
{
return IsSpacingToken(token) || token.Kind == SyntaxKind.CSharpComment;
};
protected static readonly Func<SyntaxToken, bool> IsSpacingTokenIncludingNewLinesAndComments = (token) =>
{
return IsSpacingTokenIncludingNewLines(token) || token.Kind == SyntaxKind.CSharpComment;
};
protected TokenizerBackedParser(LanguageCharacteristics<TTokenizer> language, ParserContext context)
@ -224,7 +224,19 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
protected internal bool NextIs(SyntaxKind type)
{
return NextIs(token => token != null && type == token.Kind);
// Duplicated logic with NextIs(Func...) to prevent allocation
var cur = CurrentToken;
var result = false;
if (NextToken())
{
result = (type == CurrentToken.Kind);
PutCurrentBack();
}
PutBack(cur);
EnsureCurrent();
return result;
}
protected internal bool NextIs(params SyntaxKind[] types)
@ -235,21 +247,17 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
protected internal bool NextIs(Func<SyntaxToken, bool> condition)
{
var cur = CurrentToken;
var result = false;
if (NextToken())
{
var result = condition(CurrentToken);
result = condition(CurrentToken);
PutCurrentBack();
PutBack(cur);
EnsureCurrent();
return result;
}
else
{
PutBack(cur);
EnsureCurrent();
}
return false;
PutBack(cur);
EnsureCurrent();
return result;
}
protected internal bool Was(SyntaxKind type)
@ -289,11 +297,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
protected internal IReadOnlyList<SyntaxToken> ReadWhile(Func<SyntaxToken, bool> condition)
{
if (!EnsureCurrent() || !condition(CurrentToken))
{
return Array.Empty<SyntaxToken>();
}
if (!EnsureCurrent() || !condition(CurrentToken))
{
return Array.Empty<SyntaxToken>();
}
var result = new List<SyntaxToken>();
do
{