Optimized away the expensive StringBuilder.Remove(0, 1) in CodeInlineParser

This commit is contained in:
Kris Vandermotten
2020-04-13 16:24:35 +02:00
parent abc8aa25b7
commit 4151ac2287

View File

@@ -1,47 +1,47 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using Markdig.Helpers;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
namespace Markdig.Parsers.Inlines
{
/// <summary>
/// An inline parser for a <see cref="CodeInline"/>.
/// </summary>
/// <seealso cref="Markdig.Parsers.InlineParser" />
public class CodeInlineParser : InlineParser
{
/// <summary>
/// Initializes a new instance of the <see cref="CodeInlineParser"/> class.
/// </summary>
public CodeInlineParser()
{
OpeningCharacters = new[] { '`' };
}
public override bool Match(InlineProcessor processor, ref StringSlice slice)
{
var match = slice.CurrentChar;
if (slice.PeekCharExtra(-1) == match)
{
return false;
}
var startPosition = slice.Start;
int openSticks = 0;
int closeSticks = 0;
// Match the opened sticks
char c = slice.CurrentChar;
while (c == match)
{
openSticks++;
c = slice.NextChar();
}
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using Markdig.Helpers;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
namespace Markdig.Parsers.Inlines
{
/// <summary>
/// An inline parser for a <see cref="CodeInline"/>.
/// </summary>
/// <seealso cref="Markdig.Parsers.InlineParser" />
public class CodeInlineParser : InlineParser
{
/// <summary>
/// Initializes a new instance of the <see cref="CodeInlineParser"/> class.
/// </summary>
public CodeInlineParser()
{
OpeningCharacters = new[] { '`' };
}
public override bool Match(InlineProcessor processor, ref StringSlice slice)
{
var match = slice.CurrentChar;
if (slice.PeekCharExtra(-1) == match)
{
return false;
}
var startPosition = slice.Start;
int openSticks = 0;
int closeSticks = 0;
// Match the opened sticks
char c = slice.CurrentChar;
while (c == match)
{
openSticks++;
c = slice.NextChar();
}
var builder = processor.StringBuilders.Get();
// A backtick string is a string of one or more backtick characters (`) that is neither preceded nor followed by a backtick.
@@ -53,71 +53,76 @@ namespace Markdig.Parsers.Inlines
// 2. If the resulting string both begins AND ends with a space character, but does not consist entirely
// of space characters, a single space character is removed from the front and back.
// This allows you to include code that begins or ends with backtick characters, which must be separated by
// whitespace from the opening or closing backtick strings.
bool allSpace = true;
while (c != '\0')
{
// Transform '\n' into a single space
if (c == '\n')
{
c = ' ';
}
// whitespace from the opening or closing backtick strings.
bool allSpace = true;
while (c != '\0')
{
// Transform '\n' into a single space
if (c == '\n')
{
c = ' ';
}
if (c == match)
{
do
{
closeSticks++;
c = slice.NextChar();
do
{
closeSticks++;
c = slice.NextChar();
}
while (c == match);
if (openSticks == closeSticks)
{
break;
}
allSpace = false;
builder.Append(match, closeSticks);
if (openSticks == closeSticks)
{
break;
}
allSpace = false;
builder.Append(match, closeSticks);
closeSticks = 0;
}
}
else
{
builder.Append(c);
if (c != ' ')
{
allSpace = false;
}
}
c = slice.NextChar();
}
}
bool isMatching = false;
if (closeSticks == openSticks)
{
// Remove one space from front and back if the string is not all spaces
if (!allSpace && builder.Length > 2 && builder[0] == ' ' && builder[builder.Length - 1] == ' ')
{
builder.Length--;
builder.Remove(0, 1); // More expensive, alternative is to have a double-pass algorithm
}
processor.Inline = new CodeInline()
{
Delimiter = match,
Content = builder.ToString(),
Span = new SourceSpan(processor.GetSourcePosition(startPosition, out int line, out int column), processor.GetSourcePosition(slice.Start - 1)),
Line = line,
Column = column
};
isMatching = true;
}
// Release the builder if not used
processor.StringBuilders.Release(builder);
return isMatching;
}
}
}
}
bool isMatching = false;
if (closeSticks == openSticks)
{
string content;
// Remove one space from front and back if the string is not all spaces
if (!allSpace && builder.Length > 2 && builder[0] == ' ' && builder[builder.Length - 1] == ' ')
{
content = builder.ToString(1, builder.Length - 2);
}
else
{
content = builder.ToString();
}
processor.Inline = new CodeInline()
{
Delimiter = match,
Content = content,
Span = new SourceSpan(processor.GetSourcePosition(startPosition, out int line, out int column), processor.GetSourcePosition(slice.Start - 1)),
Line = line,
Column = column
};
isMatching = true;
}
// Release the builder if not used
processor.StringBuilders.Release(builder);
return isMatching;
}
}
}