mirror of
https://github.com/xoofx/markdig.git
synced 2026-07-08 18:16:21 +00:00
Avoid allocating CodeInline.Content substrings
This commit is contained in:
45
src/Markdig/Helpers/LazySubstring.cs
Normal file
45
src/Markdig/Helpers/LazySubstring.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
// 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 System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Markdig.Helpers
|
||||
{
|
||||
internal struct LazySubstring
|
||||
{
|
||||
public string Text;
|
||||
public int Offset;
|
||||
public int Length;
|
||||
|
||||
public LazySubstring(string text)
|
||||
{
|
||||
Text = text;
|
||||
Offset = 0;
|
||||
Length = text.Length;
|
||||
}
|
||||
|
||||
public LazySubstring(string text, int offset, int length)
|
||||
{
|
||||
Debug.Assert((ulong)offset + (ulong)length < (ulong)text.Length, $"{offset}-{length} in {text}");
|
||||
Text = text;
|
||||
Offset = offset;
|
||||
Length = length;
|
||||
}
|
||||
|
||||
public ReadOnlySpan<char> AsSpan() => Text.AsSpan(Offset, Length);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (Offset != 0 || Length != Text.Length)
|
||||
{
|
||||
Text = Text.Substring(Offset, Length);
|
||||
Offset = 0;
|
||||
}
|
||||
|
||||
return Text;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using Markdig.Helpers;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Markdig.Parsers.Inlines
|
||||
{
|
||||
@@ -54,6 +55,7 @@ namespace Markdig.Parsers.Inlines
|
||||
// whitespace from the opening or closing backtick strings.
|
||||
|
||||
bool allSpace = true;
|
||||
bool containsNewLine = false;
|
||||
var contentEnd = -1;
|
||||
|
||||
while (c != '\0')
|
||||
@@ -61,10 +63,12 @@ namespace Markdig.Parsers.Inlines
|
||||
// Transform '\n' into a single space
|
||||
if (c == '\n')
|
||||
{
|
||||
containsNewLine = true;
|
||||
c = ' ';
|
||||
}
|
||||
else if (c == '\r')
|
||||
{
|
||||
containsNewLine = true;
|
||||
slice.SkipChar();
|
||||
c = slice.CurrentChar;
|
||||
continue;
|
||||
@@ -100,14 +104,19 @@ namespace Markdig.Parsers.Inlines
|
||||
{
|
||||
ReadOnlySpan<char> contentSpan = builder.AsSpan();
|
||||
|
||||
var content = containsNewLine
|
||||
? new LazySubstring(contentSpan.ToString())
|
||||
: new LazySubstring(slice.Text, contentStart, contentSpan.Length);
|
||||
|
||||
Debug.Assert(contentSpan.SequenceEqual(content.AsSpan()));
|
||||
|
||||
// Remove one space from front and back if the string is not all spaces
|
||||
if (!allSpace && contentSpan.Length > 2 && contentSpan[0] == ' ' && contentSpan[contentSpan.Length - 1] == ' ')
|
||||
{
|
||||
contentSpan = contentSpan.Slice(1, contentSpan.Length - 2);
|
||||
content.Offset++;
|
||||
content.Length -= 2;
|
||||
}
|
||||
|
||||
string content = contentSpan.ToString();
|
||||
|
||||
int delimiterCount = Math.Min(openSticks, closeSticks);
|
||||
var spanStart = processor.GetSourcePosition(startPosition, out int line, out int column);
|
||||
var spanEnd = processor.GetSourcePosition(slice.Start - 1);
|
||||
|
||||
@@ -22,11 +22,11 @@ namespace Markdig.Renderers.Html.Inlines
|
||||
}
|
||||
if (renderer.EnableHtmlEscape)
|
||||
{
|
||||
renderer.WriteEscape(obj.Content);
|
||||
renderer.WriteEscape(obj.ContentSpan);
|
||||
}
|
||||
else
|
||||
{
|
||||
renderer.Write(obj.Content);
|
||||
renderer.Write(obj.ContentSpan);
|
||||
}
|
||||
if (renderer.EnableHtmlForInline)
|
||||
{
|
||||
|
||||
@@ -15,15 +15,16 @@ namespace Markdig.Renderers.Normalize.Inlines
|
||||
protected override void Write(NormalizeRenderer renderer, CodeInline obj)
|
||||
{
|
||||
var delimiterCount = 0;
|
||||
for (var i = 0; i < obj.Content!.Length; i++)
|
||||
string content = obj.Content;
|
||||
for (var i = 0; i < content.Length; i++)
|
||||
{
|
||||
var index = obj.Content.IndexOf(obj.Delimiter, i);
|
||||
var index = content.IndexOf(obj.Delimiter, i);
|
||||
if (index == -1) break;
|
||||
|
||||
var count = 1;
|
||||
for (i = index + 1; i < obj.Content.Length; i++)
|
||||
for (i = index + 1; i < content.Length; i++)
|
||||
{
|
||||
if (obj.Content[i] == obj.Delimiter) count++;
|
||||
if (content[i] == obj.Delimiter) count++;
|
||||
else break;
|
||||
}
|
||||
|
||||
@@ -32,14 +33,14 @@ namespace Markdig.Renderers.Normalize.Inlines
|
||||
}
|
||||
var delimiterRun = new string(obj.Delimiter, delimiterCount + 1);
|
||||
renderer.Write(delimiterRun);
|
||||
if (obj.Content.Length != 0)
|
||||
if (content.Length != 0)
|
||||
{
|
||||
if (obj.Content[0] == obj.Delimiter)
|
||||
if (content[0] == obj.Delimiter)
|
||||
{
|
||||
renderer.Write(' ');
|
||||
}
|
||||
renderer.Write(obj.Content);
|
||||
if (obj.Content[obj.Content.Length - 1] == obj.Delimiter)
|
||||
renderer.Write(content);
|
||||
if (content[content.Length - 1] == obj.Delimiter)
|
||||
{
|
||||
renderer.Write(' ');
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Markdig.Renderers.Roundtrip.Inlines
|
||||
{
|
||||
var delimiterRun = new string(obj.Delimiter, obj.DelimiterCount);
|
||||
renderer.Write(delimiterRun);
|
||||
if (obj.Content is { Length: > 0 })
|
||||
if (!obj.ContentSpan.IsEmpty)
|
||||
{
|
||||
renderer.Write(obj.ContentWithTrivia);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Helpers;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Markdig.Syntax.Inlines
|
||||
@@ -17,9 +18,13 @@ namespace Markdig.Syntax.Inlines
|
||||
private TriviaProperties? _trivia => GetTrivia<TriviaProperties>();
|
||||
private TriviaProperties Trivia => GetOrSetTrivia<TriviaProperties>();
|
||||
|
||||
public CodeInline(string content)
|
||||
private LazySubstring _content;
|
||||
|
||||
public CodeInline(string content) : this(new LazySubstring(content)) { }
|
||||
|
||||
internal CodeInline(LazySubstring content)
|
||||
{
|
||||
Content = content;
|
||||
_content = content;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -35,7 +40,13 @@ namespace Markdig.Syntax.Inlines
|
||||
/// <summary>
|
||||
/// Gets or sets the content of the span.
|
||||
/// </summary>
|
||||
public string Content { get; set; }
|
||||
public string Content
|
||||
{
|
||||
get => _content.ToString();
|
||||
set => _content = new LazySubstring(value ?? string.Empty);
|
||||
}
|
||||
|
||||
public ReadOnlySpan<char> ContentSpan => _content.AsSpan();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the content with trivia and whitespace.
|
||||
|
||||
Reference in New Issue
Block a user