Skip to content

Commit

Permalink
feat: 性能优化。
Browse files Browse the repository at this point in the history
  • Loading branch information
CYJB committed Mar 21, 2024
1 parent 1229376 commit f126e8b
Show file tree
Hide file tree
Showing 17 changed files with 259 additions and 265 deletions.
5 changes: 3 additions & 2 deletions Cyjb.Markdown/ParseBlock/BlockParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,8 @@ public Document Parse()
int end = token.Span.Start;
CloseProcessor(document, end);
// 合并标题引用。
if (headingReferences.Count > 0)
int count = headingReferences.Count;
if (count > 0)
{
foreach (var pair in headingReferences)
{
Expand All @@ -182,7 +183,7 @@ public Document Parse()
doc.Accept(walker);
}
// 填充标题的链接声明
if (headingReferences.Count > 0)
if (count > 0)
{
foreach (var tuple in headingReferences.Values)
{
Expand Down
84 changes: 53 additions & 31 deletions Cyjb.Markdown/ParseBlock/BlockText.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public int Start
}
}
/// <summary>
/// 获取文本的结束位置。
/// 获取文本的结束位置。}
/// </summary>
public int End
{
Expand Down Expand Up @@ -108,33 +108,6 @@ public bool IsSingleLine()
return true;
}

/// <summary>
/// 返回指定索引的文本。
/// </summary>
/// <param name="index">要检查的索引。</param>
/// <returns>指定索引的文本。</returns>
public char this[int index]
{
get
{
int count = tokens.Count;
for (int i = 0; i < count; i++)
{
StringView text = tokens[i].Text;
int textLen = text.Length;
if (index >= textLen)
{
index -= textLen;
}
else
{
return text[index];
}
}
return SourceReader.InvalidCharacter;
}
}

/// <summary>
/// 移除起始位置的多个字符。
/// </summary>
Expand Down Expand Up @@ -466,8 +439,57 @@ public StringView ToStringView()
{
text.Append(tokens[i].Text.AsSpan());
}
string result = text.ToString();
StringBuilderPool.Return(text);
return result;
return StringBuilderPool.GetStringAndReturn(text);
}

/// <summary>
/// 返回当前对象的字符串视图表示形式。
/// </summary>
/// <param name="start">转换为字符串视图的起始索引。</param>
/// <returns>当前对象的字符串视图表示形式。</returns>
public StringView ToStringView(int start)
{
int count = tokens.Count;
if (count == 0)
{
return StringView.Empty;
}
else if (count == 1)
{
return tokens[0].Text.Substring(start);
}
// 优先连接字符串视图。
StringView view = StringView.Empty;
int i = 0;
for (; i < count; i++)
{
StringView curView = tokens[i].Text;
if (curView.Length <= start)
{
start -= curView.Length;
continue;
}
start = 0;
if (view.TryConcat(curView.Substring(start), out var newView))
{
view = newView;
}
else
{
break;
}
}
if (i >= count)
{
return view;
}
// 存在无法连接的字符串,改为使用 StringBuilder 拼接。
StringBuilder text = StringBuilderPool.Rent(length);
text.Append(view.AsSpan());
for (; i < count; i++)
{
text.Append(tokens[i].Text.AsSpan());
}
return StringBuilderPool.GetStringAndReturn(text);
}
}
28 changes: 13 additions & 15 deletions Cyjb.Markdown/ParseBlock/Processors/LinkDefinitionParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ public List<LinkDefinition> GetDefinitions()
/// <returns>是否解析成功。</returns>
private bool ParseStartDefinition(ref ReadOnlySpan<char> text, TextSpan span)
{
MarkdownUtil.TrimStart(ref text);
text = text.TrimStart(MarkdownUtil.Whitespace);
if (text.Length == 0 || text[0] != '[')
{
return false;
Expand Down Expand Up @@ -224,7 +224,7 @@ private bool ParseLabel(ref ReadOnlySpan<char> text)
idx = text.Length;
}
builder.Add(text[..idx]);
text = text[idx..];
text = text.Slice(idx);
if (text.Length == 0)
{
return true;
Expand All @@ -240,17 +240,16 @@ private bool ParseLabel(ref ReadOnlySpan<char> text)
return false;
}
// 链接定义中不允许使用空白的标签。
ReadOnlySpan<char> labelSpan = builder.AsSpan();
MarkdownUtil.TrimStart(ref labelSpan);
ReadOnlySpan<char> labelSpan = builder.AsSpan().TrimStart(MarkdownUtil.Whitespace);
if (labelSpan.Length == 0)
{
return false;
}
label = builder.ToString();
text = text[2..];
text = text.Slice(2);
state = State.Destination;
// 移除后面的空白,避免空白字符串进入到解析链接定义的流程中。
MarkdownUtil.TrimStart(ref text);
text = text.TrimStart(MarkdownUtil.Whitespace);
return true;
}

Expand All @@ -262,7 +261,7 @@ private bool ParseLabel(ref ReadOnlySpan<char> text)
/// <returns>是否解析成功。</returns>
private bool ParseDestination(ref ReadOnlySpan<char> text, TextSpan span)
{
MarkdownUtil.TrimStart(ref text);
text = text.TrimStart(MarkdownUtil.Whitespace);
// 解析链接目标,在定义中不允许使用空目标。
if (!MarkdownUtil.TryParseLinkDestination(ref text, out destination) || destination == null)
{
Expand All @@ -286,7 +285,7 @@ private bool ParseDestination(ref ReadOnlySpan<char> text, TextSpan span)
state = State.StartTitleOrAttr;
title = null;
// 移除后面的空白,避免空白字符串进入到解析链接标题起始的流程中。
MarkdownUtil.TrimStart(ref text);
text = text.TrimStart(MarkdownUtil.Whitespace);
return true;
}

Expand All @@ -297,7 +296,7 @@ private bool ParseDestination(ref ReadOnlySpan<char> text, TextSpan span)
/// <returns>是否解析成功。</returns>
private bool ParseStartTitleOrAttr(ref ReadOnlySpan<char> text)
{
MarkdownUtil.TrimStart(ref text);
text = text.TrimStart(MarkdownUtil.Whitespace);
if (text.Length == 0)
{
state = State.StartDefinition;
Expand Down Expand Up @@ -340,9 +339,8 @@ private bool ParseStartTitleOrAttr(ref ReadOnlySpan<char> text)
/// </summary>
private void EnterAttribute(ref ReadOnlySpan<char> text)
{
text = text[1..];
// 移除掉行首的空白,避免被识别为空行。
MarkdownUtil.TrimStart(ref text);
text = text.Slice(1).TrimStart(MarkdownUtil.Whitespace);
state = State.Attributes;
attributes?.Clear();
}
Expand Down Expand Up @@ -430,11 +428,11 @@ private bool ParseTitle(ref ReadOnlySpan<char> text, TextSpan span)
/// <returns>是否解析成功。</returns>
private bool ParseStartAttribute(ref ReadOnlySpan<char> text)
{
MarkdownUtil.TrimStart(ref text);
text = text.TrimStart(MarkdownUtil.Whitespace);
if (text.Length > 0 && text[0] == '{')
{
EnterAttribute(ref text);
MarkdownUtil.TrimStart(ref text);
text = text.TrimStart(MarkdownUtil.Whitespace);
}
else
{
Expand All @@ -453,7 +451,7 @@ private bool ParseStartAttribute(ref ReadOnlySpan<char> text)
/// <returns>是否解析成功。</returns>
private bool ParseAttributes(ref ReadOnlySpan<char> text, TextSpan span)
{
MarkdownUtil.TrimStart(ref text);
text = text.TrimStart(MarkdownUtil.Whitespace);
if (text.Length == 0)
{
// 属性解析过程中的空白行会导致链接解析失败。
Expand Down Expand Up @@ -514,7 +512,7 @@ private bool ParseAttributeEnd(ref ReadOnlySpan<char> text, TextSpan span)
{
// 属性解析结束,要求属性后不能有其它非空白字符。
text = text[1..];
MarkdownUtil.TrimStart(ref text);
text = text.TrimStart(MarkdownUtil.Whitespace);
if (text.Length > 0)
{
// 后面有非空白字符,清理失效数据。
Expand Down
1 change: 0 additions & 1 deletion Cyjb.Markdown/ParseBlock/Processors/ParagraphProcessor.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System.Text;
using Cyjb.Markdown.ParseInline;
using Cyjb.Markdown.Syntax;
using Cyjb.Text;
Expand Down
12 changes: 1 addition & 11 deletions Cyjb.Markdown/ParseBlock/Processors/SetextHeadingProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,23 +137,13 @@ public IEnumerable<BlockProcessor> TryStart(BlockParser parser, BlockLine line,
// 未找到起始 {。
return null;
}
ValueList<char> list = new(stackalloc char[ValueList.StackallocCharSizeLimit]);
for (int i = startIdx; i < text.Length; i++)
{
list.Add(text[i]);
}
ReadOnlySpan<char> span = list.AsSpan();
ReadOnlySpan<char> span = text.ToStringView(startIdx).AsSpan();
HtmlAttributeList? attrs = MarkdownUtil.ParseAttributes(ref span);
if (attrs != null)
{
list.Dispose();
// 移除行中不需要的部分。
text.RemoteEnd(text.Length - startIdx);
}
else
{
list.Dispose();
}
return attrs;
}
}
Expand Down
5 changes: 3 additions & 2 deletions Cyjb.Markdown/ParseBlock/Processors/TableProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,10 @@ public override void AddLine(BlockLine line)
/// <param name="parser">行内节点的解析器。</param>
public override void ParseInline(InlineParser parser)
{
foreach (CellInfo info in cellInfos)
int count = cellInfos.Count;
for (int i = 0; i < count; i++)
{
info.ParseInline(parser);
cellInfos[i].ParseInline(parser);
}
}

Expand Down
25 changes: 12 additions & 13 deletions Cyjb.Markdown/ParseInline/InlineLexer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,9 @@ private string ParseAutolink(out HtmlAttributeList? attrs)
int idx = Text.IndexOf('>');
string url = Text[1..idx].ToString();
// 解析属性。
StringView attrText = Text[(idx + 1)..];
ReadOnlySpan<char> attrText = Text.AsSpan(idx + 1);
int attrLen = attrText.Length;
if (options.UseLinkAttributes && attrText.Length > 0 &&
attrText[0] == '{' && attrText[^1] == '}')
if (options.UseLinkAttributes && attrLen > 0 && attrText[0] == '{' && attrText[^1] == '}')
{
attrs = MarkdownUtil.ParseAttributes(ref attrText);
if (attrs == null || attrText.Length > 0)
Expand Down Expand Up @@ -309,8 +308,9 @@ private void LinkLabelAction()
{
InlineParser parser = (InlineParser)SharedContext!;
// 标签不为空,使用标签本身。
ReadOnlySpan<char> text = Text[2..];
if (!MarkdownUtil.TryParseLinkLabel(ref text, out string? label))
ReadOnlySpan<char> text = Text.AsSpan(2);
ReadOnlySpan<char> label = ReadOnlySpan<char>.Empty;
if (!MarkdownUtil.TryParseLinkLabel(ref text, ref label))
{
Reject();
return;
Expand All @@ -320,7 +320,7 @@ private void LinkLabelAction()
// 标签为空,将前面的文本作为标签使用。
label = parser.GetCurrentLinkText(Start);
}
if (label != null && parser.TryGetLinkDefine(LinkUtil.NormalizeLabel(label), out LinkDefinition? define))
if (!label.IsEmpty && parser.TryGetLinkDefine(LinkUtil.NormalizeLabel(label), out LinkDefinition? define))
{
Accept(define);
}
Expand All @@ -337,8 +337,8 @@ private void LinkLabelAction()
[LexerSymbol(@"<LinkClose>]\({WS}{LinkDestination}?({WS_1}{LinkTitle})?{WS}\){ExtAttr}?", Kind = InlineKind.LinkClose)]
private void LinkBodyAction()
{
StringView text = Text[2..];
MarkdownUtil.TrimStart(ref text);
ReadOnlySpan<char> text = Text.AsSpan(2);
text = text.TrimStart(MarkdownUtil.Whitespace);
// 解析链接目标和标题。
if (!MarkdownUtil.TryParseLinkDestination(ref text, out string? destination))
{
Expand All @@ -351,19 +351,18 @@ private void LinkBodyAction()
Reject();
return;
}
MarkdownUtil.TrimStart(ref text);
text = text.TrimStart(MarkdownUtil.Whitespace);
// 链接目标和标题要求以 ) 闭合。
if (text.Length == 0 || text[0] != ')')
if (text.IsEmpty || text[0] != ')')
{
Reject();
return;
}
text = text[1..];
text = text.Slice(1);
LinkBody body = new(destination, title);
// 解析属性。
int attrLen = text.Length;
if (options.UseLinkAttributes && text.Length > 0 &&
text[0] == '{' && text[^1] == '}')
if (options.UseLinkAttributes && attrLen > 0 && text[0] == '{' && text[^1] == '}')
{
HtmlAttributeList? attrs = MarkdownUtil.ParseAttributes(ref text);
if (attrs == null || text.Length > 0)
Expand Down
12 changes: 6 additions & 6 deletions Cyjb.Markdown/ParseInline/InlineParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ private bool ParseCloseBracket(Token<InlineKind> token)
return true;
}
else if (!opener.BracketAfter && reader.Peek() != '[' &&
linkDefines.TryGetValue(LinkUtil.NormalizeLabel(GetCurrentLinkText(reader.Index - 1)!), out LinkDefinition? linkDefine2))
linkDefines.TryGetValue(LinkUtil.NormalizeLabel(GetCurrentLinkText(reader.Index - 1)), out LinkDefinition? linkDefine2))
{
// 是 ],后面没有 URL 或标签。
// 如果没有更多待匹配的括号,缺失第二个 label 时会将当前文本当作标签解析。
Expand Down Expand Up @@ -266,12 +266,12 @@ private bool TryParseFootnote(BracketInfo opener, [MaybeNullWhen(false)] out Foo
{
return false;
}
string label = GetCurrentLinkText(reader.Index - 1)!;
ReadOnlySpan<char> label = GetCurrentLinkText(reader.Index - 1);
if (!MarkdownUtil.IsFootnotesLabel(label))
{
return false;
}
return footnotes.TryGetValue(LinkUtil.NormalizeLabel(label.AsSpan(1)), out footnote);
return footnotes.TryGetValue(LinkUtil.NormalizeLabel(label.Slice(1)), out footnote);
}

/// <summary>
Expand Down Expand Up @@ -451,14 +451,14 @@ internal bool TryGetLinkDefine(string label, [MaybeNullWhen(false)] out LinkDefi
/// </summary>
/// <param name="endIndex">链接文本的结束索引(不含)。</param>
/// <returns>当前的链接文本。</returns>
internal string? GetCurrentLinkText(int endIndex)
internal StringView GetCurrentLinkText(int endIndex)
{
if (brackets.Count == 0)
{
return null;
return StringView.Empty;
}
BracketInfo info = brackets.Peek();
return reader.ReadBlock(info.StartMark.Index, endIndex - info.StartMark.Index).ToString();
return reader.ReadBlock(info.StartMark.Index, endIndex - info.StartMark.Index);
}

/// <summary>
Expand Down
Loading

0 comments on commit f126e8b

Please sign in to comment.