diff --git a/src/Markdig.Tests/TestMarkdigCoreApi.cs b/src/Markdig.Tests/TestMarkdigCoreApi.cs
index 415046646..ab26d4137 100644
--- a/src/Markdig.Tests/TestMarkdigCoreApi.cs
+++ b/src/Markdig.Tests/TestMarkdigCoreApi.cs
@@ -11,11 +11,14 @@ public class TestMarkdigCoreApi
[Test]
public void TestToHtml()
{
- string html = Markdown.ToHtml("This is a text with some *emphasis*");
- Assert.AreEqual("
This is a text with some emphasis
\n", html);
-
- html = Markdown.ToHtml("This is a text with a https://link.tld/");
- Assert.AreNotEqual("This is a text with a https://link.tld/
\n", html);
+ for (int i = 0; i < 5; i++)
+ {
+ string html = Markdown.ToHtml("This is a text with some *emphasis*");
+ Assert.AreEqual("This is a text with some emphasis
\n", html);
+
+ html = Markdown.ToHtml("This is a text with a https://link.tld/");
+ Assert.AreNotEqual("This is a text with a https://link.tld/
\n", html);
+ }
}
[Test]
@@ -24,49 +27,66 @@ public void TestToHtmlWithPipeline()
var pipeline = new MarkdownPipelineBuilder()
.Build();
- string html = Markdown.ToHtml("This is a text with some *emphasis*", pipeline);
- Assert.AreEqual("This is a text with some emphasis
\n", html);
+ for (int i = 0; i < 5; i++)
+ {
+ string html = Markdown.ToHtml("This is a text with some *emphasis*", pipeline);
+ Assert.AreEqual("This is a text with some emphasis
\n", html);
- html = Markdown.ToHtml("This is a text with a https://link.tld/", pipeline);
- Assert.AreNotEqual("This is a text with a https://link.tld/
\n", html);
+ html = Markdown.ToHtml("This is a text with a https://link.tld/", pipeline);
+ Assert.AreNotEqual("This is a text with a https://link.tld/
\n", html);
+ }
pipeline = new MarkdownPipelineBuilder()
.UseAdvancedExtensions()
.Build();
- html = Markdown.ToHtml("This is a text with a https://link.tld/", pipeline);
- Assert.AreEqual("This is a text with a https://link.tld/
\n", html);
+ for (int i = 0; i < 5; i++)
+ {
+ string html = Markdown.ToHtml("This is a text with a https://link.tld/", pipeline);
+ Assert.AreEqual("This is a text with a https://link.tld/
\n", html);
+ }
}
[Test]
public void TestToHtmlWithWriter()
{
- StringWriter writer = new StringWriter();
+ var writer = new StringWriter();
- _ = Markdown.ToHtml("This is a text with some *emphasis*", writer);
- string html = writer.ToString();
- Assert.AreEqual("This is a text with some emphasis
\n", html);
+ for (int i = 0; i < 5; i++)
+ {
+ _ = Markdown.ToHtml("This is a text with some *emphasis*", writer);
+ string html = writer.ToString();
+ Assert.AreEqual("This is a text with some emphasis
\n", html);
+ writer.GetStringBuilder().Length = 0;
+ }
writer = new StringWriter();
var pipeline = new MarkdownPipelineBuilder()
.UseAdvancedExtensions()
.Build();
- _ = Markdown.ToHtml("This is a text with a https://link.tld/", writer, pipeline);
- html = writer.ToString();
- Assert.AreEqual("This is a text with a https://link.tld/
\n", html);
-
+ for (int i = 0; i < 5; i++)
+ {
+ _ = Markdown.ToHtml("This is a text with a https://link.tld/", writer, pipeline);
+ string html = writer.ToString();
+ Assert.AreEqual("This is a text with a https://link.tld/
\n", html);
+ writer.GetStringBuilder().Length = 0;
+ }
}
[Test]
public void TestConvert()
{
- StringWriter writer = new StringWriter();
- HtmlRenderer renderer = new HtmlRenderer(writer);
+ var writer = new StringWriter();
+ var renderer = new HtmlRenderer(writer);
- _ = Markdown.Convert("This is a text with some *emphasis*", renderer);
- string html = writer.ToString();
- Assert.AreEqual("This is a text with some emphasis
\n", html);
+ for (int i = 0; i < 5; i++)
+ {
+ _ = Markdown.Convert("This is a text with some *emphasis*", renderer);
+ string html = writer.ToString();
+ Assert.AreEqual("This is a text with some emphasis
\n", html);
+ writer.GetStringBuilder().Length = 0;
+ }
writer = new StringWriter();
renderer = new HtmlRenderer(writer);
@@ -74,9 +94,13 @@ public void TestConvert()
.UseAdvancedExtensions()
.Build();
- _ = Markdown.Convert("This is a text with a https://link.tld/", renderer, pipeline);
- html = writer.ToString();
- Assert.AreEqual("This is a text with a https://link.tld/
\n", html);
+ for (int i = 0; i < 5; i++)
+ {
+ _ = Markdown.Convert("This is a text with a https://link.tld/", renderer, pipeline);
+ string html = writer.ToString();
+ Assert.AreEqual("This is a text with a https://link.tld/
\n", html);
+ writer.GetStringBuilder().Length = 0;
+ }
}
[Test]
@@ -88,62 +112,77 @@ public void TestParse()
.UsePreciseSourceLocation()
.Build();
- MarkdownDocument document = Markdown.Parse(markdown, pipeline);
-
- Assert.AreEqual(1, document.LineCount);
- Assert.AreEqual(markdown.Length, document.Span.Length);
- Assert.AreEqual(1, document.LineStartIndexes.Count);
- Assert.AreEqual(0, document.LineStartIndexes[0]);
-
- Assert.AreEqual(1, document.Count);
- ParagraphBlock paragraph = document[0] as ParagraphBlock;
- Assert.NotNull(paragraph);
- Assert.AreEqual(markdown.Length, paragraph.Span.Length);
- LiteralInline literal = paragraph.Inline.FirstChild as LiteralInline;
- Assert.NotNull(literal);
- Assert.AreEqual("This is a text with some ", literal.ToString());
- EmphasisInline emphasis = literal.NextSibling as EmphasisInline;
- Assert.NotNull(emphasis);
- Assert.AreEqual("*emphasis*".Length, emphasis.Span.Length);
- LiteralInline emphasisLiteral = emphasis.FirstChild as LiteralInline;
- Assert.NotNull(emphasisLiteral);
- Assert.AreEqual("emphasis", emphasisLiteral.ToString());
- Assert.Null(emphasisLiteral.NextSibling);
- Assert.Null(emphasis.NextSibling);
+ for (int i = 0; i < 5; i++)
+ {
+ MarkdownDocument document = Markdown.Parse(markdown, pipeline);
+
+ Assert.AreEqual(1, document.LineCount);
+ Assert.AreEqual(markdown.Length, document.Span.Length);
+ Assert.AreEqual(1, document.LineStartIndexes.Count);
+ Assert.AreEqual(0, document.LineStartIndexes[0]);
+
+ Assert.AreEqual(1, document.Count);
+ ParagraphBlock paragraph = document[0] as ParagraphBlock;
+ Assert.NotNull(paragraph);
+ Assert.AreEqual(markdown.Length, paragraph.Span.Length);
+ LiteralInline literal = paragraph.Inline.FirstChild as LiteralInline;
+ Assert.NotNull(literal);
+ Assert.AreEqual("This is a text with some ", literal.ToString());
+ EmphasisInline emphasis = literal.NextSibling as EmphasisInline;
+ Assert.NotNull(emphasis);
+ Assert.AreEqual("*emphasis*".Length, emphasis.Span.Length);
+ LiteralInline emphasisLiteral = emphasis.FirstChild as LiteralInline;
+ Assert.NotNull(emphasisLiteral);
+ Assert.AreEqual("emphasis", emphasisLiteral.ToString());
+ Assert.Null(emphasisLiteral.NextSibling);
+ Assert.Null(emphasis.NextSibling);
+ }
}
[Test]
public void TestNormalize()
{
- string normalized = Markdown.Normalize("Heading\n=======");
- Assert.AreEqual("# Heading", normalized);
+ for (int i = 0; i < 5; i++)
+ {
+ string normalized = Markdown.Normalize("Heading\n=======");
+ Assert.AreEqual("# Heading", normalized);
+ }
}
[Test]
public void TestNormalizeWithWriter()
{
- StringWriter writer = new StringWriter();
-
- _ = Markdown.Normalize("Heading\n=======", writer);
- string normalized = writer.ToString();
- Assert.AreEqual("# Heading", normalized);
+ for (int i = 0; i < 5; i++)
+ {
+ var writer = new StringWriter();
+
+ _ = Markdown.Normalize("Heading\n=======", writer);
+ string normalized = writer.ToString();
+ Assert.AreEqual("# Heading", normalized);
+ }
}
[Test]
public void TestToPlainText()
{
- string plainText = Markdown.ToPlainText("*Hello*, [world](http://example.com)!");
- Assert.AreEqual("Hello, world!\n", plainText);
+ for (int i = 0; i < 5; i++)
+ {
+ string plainText = Markdown.ToPlainText("*Hello*, [world](http://example.com)!");
+ Assert.AreEqual("Hello, world!\n", plainText);
+ }
}
[Test]
public void TestToPlainTextWithWriter()
{
- StringWriter writer = new StringWriter();
-
- _ = Markdown.ToPlainText("*Hello*, [world](http://example.com)!", writer);
- string plainText = writer.ToString();
- Assert.AreEqual("Hello, world!\n", plainText);
+ for (int i = 0; i < 5; i++)
+ {
+ var writer = new StringWriter();
+
+ _ = Markdown.ToPlainText("*Hello*, [world](http://example.com)!", writer);
+ string plainText = writer.ToString();
+ Assert.AreEqual("Hello, world!\n", plainText);
+ }
}
}
}
diff --git a/src/Markdig/Markdown.cs b/src/Markdig/Markdown.cs
index 3c04abbee..f31bf760f 100644
--- a/src/Markdig/Markdown.cs
+++ b/src/Markdig/Markdown.cs
@@ -23,6 +23,25 @@ public static partial class Markdown
{
public static readonly string Version = ((AssemblyFileVersionAttribute) typeof(Markdown).Assembly.GetCustomAttributes(typeof(AssemblyFileVersionAttribute), false)[0]).Version;
+ private static readonly MarkdownPipeline _defaultPipeline = new MarkdownPipelineBuilder().Build();
+ private static readonly MarkdownPipeline _defaultTrackTriviaPipeline = new MarkdownPipelineBuilder().EnableTrackTrivia().Build();
+
+ private static MarkdownPipeline GetPipeline(MarkdownPipeline? pipeline, string markdown)
+ {
+ if (pipeline is null)
+ {
+ return _defaultPipeline;
+ }
+
+ var selfPipeline = pipeline.Extensions.Find();
+ if (selfPipeline is not null)
+ {
+ return selfPipeline.CreatePipelineFromInput(markdown);
+ }
+ return pipeline;
+ }
+
+
///
/// Normalizes the specified markdown to a normalized markdown text.
///
@@ -49,14 +68,15 @@ public static string Normalize(string markdown, NormalizeOptions? options = null
/// A normalized markdown text.
public static MarkdownDocument Normalize(string markdown, TextWriter writer, NormalizeOptions? options = null, MarkdownPipeline? pipeline = null, MarkdownParserContext? context = null)
{
- pipeline ??= new MarkdownPipelineBuilder().Build();
- pipeline = CheckForSelfPipeline(pipeline, markdown);
+ if (markdown is null) ThrowHelper.ArgumentNullException_markdown();
+
+ pipeline = GetPipeline(pipeline, markdown);
+
+ var document = MarkdownParser.Parse(markdown, pipeline, context);
- // We override the renderer with our own writer
var renderer = new NormalizeRenderer(writer, options);
pipeline.Setup(renderer);
- var document = Parse(markdown, pipeline, context);
renderer.Render(document);
writer.Flush();
@@ -74,18 +94,12 @@ public static MarkdownDocument Normalize(string markdown, TextWriter writer, Nor
public static string ToHtml(string markdown, MarkdownPipeline? pipeline = null, MarkdownParserContext? context = null)
{
if (markdown is null) ThrowHelper.ArgumentNullException_markdown();
- pipeline ??= new MarkdownPipelineBuilder().Build();
- pipeline = CheckForSelfPipeline(pipeline, markdown);
- var renderer = pipeline.GetCacheableHtmlRenderer();
+ pipeline = GetPipeline(pipeline, markdown);
- var document = Parse(markdown, pipeline, context);
- renderer.Render(document);
- renderer.Writer.Flush();
+ var document = MarkdownParser.Parse(markdown, pipeline, context);
- string html = renderer.Writer.ToString()!;
- pipeline.ReleaseCacheableHtmlRenderer(renderer);
- return html;
+ return ToHtml(document, pipeline);
}
///
@@ -95,19 +109,19 @@ public static string ToHtml(string markdown, MarkdownPipeline? pipeline = null,
/// The pipeline used for the conversion.
/// The result of the conversion
/// if markdown document variable is null
- public static string ToHtml(this MarkdownDocument document, MarkdownPipeline pipeline = null)
+ public static string ToHtml(this MarkdownDocument document, MarkdownPipeline? pipeline = null)
{
- if (document == null) ThrowHelper.ArgumentNullException(nameof(document));
- pipeline ??= new MarkdownPipelineBuilder().Build();
+ if (document is null) ThrowHelper.ArgumentNullException(nameof(document));
+
+ pipeline ??= _defaultPipeline;
- var renderer = pipeline.GetCacheableHtmlRenderer();
+ using var rentedRenderer = pipeline.RentHtmlRenderer();
+ HtmlRenderer renderer = rentedRenderer.Instance;
renderer.Render(document);
renderer.Writer.Flush();
- string html = renderer.Writer.ToString();
- pipeline.ReleaseCacheableHtmlRenderer(renderer);
- return html;
+ return renderer.Writer.ToString() ?? string.Empty;
}
///
@@ -123,14 +137,14 @@ public static MarkdownDocument ToHtml(string markdown, TextWriter writer, Markdo
{
if (markdown is null) ThrowHelper.ArgumentNullException_markdown();
if (writer is null) ThrowHelper.ArgumentNullException_writer();
- pipeline ??= new MarkdownPipelineBuilder().Build();
- pipeline = CheckForSelfPipeline(pipeline, markdown);
- // We override the renderer with our own writer
- var renderer = new HtmlRenderer(writer);
- pipeline.Setup(renderer);
+ pipeline = GetPipeline(pipeline, markdown);
+
+ var document = MarkdownParser.Parse(markdown, pipeline, context);
+
+ using var rentedRenderer = pipeline.RentHtmlRenderer(writer);
+ HtmlRenderer renderer = rentedRenderer.Instance;
- var document = Parse(markdown, pipeline, context);
renderer.Render(document);
writer.Flush();
@@ -149,10 +163,11 @@ public static object Convert(string markdown, IMarkdownRenderer renderer, Markdo
{
if (markdown is null) ThrowHelper.ArgumentNullException_markdown();
if (renderer is null) ThrowHelper.ArgumentNullException(nameof(renderer));
- pipeline ??= new MarkdownPipelineBuilder().Build();
- pipeline = CheckForSelfPipeline(pipeline, markdown);
- var document = Parse(markdown, pipeline, context);
+ pipeline = GetPipeline(pipeline, markdown);
+
+ var document = MarkdownParser.Parse(markdown, pipeline, context);
+
pipeline.Setup(renderer);
return renderer.Render(document);
}
@@ -168,9 +183,7 @@ public static MarkdownDocument Parse(string markdown, bool trackTrivia = false)
{
if (markdown is null) ThrowHelper.ArgumentNullException_markdown();
- MarkdownPipeline? pipeline = trackTrivia ? new MarkdownPipelineBuilder()
- .EnableTrackTrivia()
- .Build() : null;
+ MarkdownPipeline? pipeline = trackTrivia ? _defaultTrackTriviaPipeline : null;
return Parse(markdown, pipeline);
}
@@ -186,20 +199,10 @@ public static MarkdownDocument Parse(string markdown, bool trackTrivia = false)
public static MarkdownDocument Parse(string markdown, MarkdownPipeline? pipeline, MarkdownParserContext? context = null)
{
if (markdown is null) ThrowHelper.ArgumentNullException_markdown();
- pipeline ??= new MarkdownPipelineBuilder().Build();
- pipeline = CheckForSelfPipeline(pipeline, markdown);
- return MarkdownParser.Parse(markdown, pipeline, context);
- }
+ pipeline = GetPipeline(pipeline, markdown);
- private static MarkdownPipeline CheckForSelfPipeline(MarkdownPipeline pipeline, string markdown)
- {
- var selfPipeline = pipeline.Extensions.Find();
- if (selfPipeline != null)
- {
- return selfPipeline.CreatePipelineFromInput(markdown);
- }
- return pipeline;
+ return MarkdownParser.Parse(markdown, pipeline, context);
}
///
@@ -213,10 +216,12 @@ private static MarkdownPipeline CheckForSelfPipeline(MarkdownPipeline pipeline,
/// if reader or writer variable are null
public static MarkdownDocument ToPlainText(string markdown, TextWriter writer, MarkdownPipeline? pipeline = null, MarkdownParserContext? context = null)
{
- if (markdown == null) ThrowHelper.ArgumentNullException_markdown();
- if (writer == null) ThrowHelper.ArgumentNullException_writer();
- pipeline ??= new MarkdownPipelineBuilder().Build();
- pipeline = CheckForSelfPipeline(pipeline, markdown);
+ if (markdown is null) ThrowHelper.ArgumentNullException_markdown();
+ if (writer is null) ThrowHelper.ArgumentNullException_writer();
+
+ pipeline = GetPipeline(pipeline, markdown);
+
+ var document = MarkdownParser.Parse(markdown, pipeline, context);
// We override the renderer with our own writer
var renderer = new HtmlRenderer(writer)
@@ -227,7 +232,6 @@ public static MarkdownDocument ToPlainText(string markdown, TextWriter writer, M
};
pipeline.Setup(renderer);
- var document = Parse(markdown, pipeline, context);
renderer.Render(document);
writer.Flush();
@@ -244,7 +248,7 @@ public static MarkdownDocument ToPlainText(string markdown, TextWriter writer, M
/// if markdown variable is null
public static string ToPlainText(string markdown, MarkdownPipeline? pipeline = null, MarkdownParserContext? context = null)
{
- if (markdown == null) ThrowHelper.ArgumentNullException_markdown();
+ if (markdown is null) ThrowHelper.ArgumentNullException_markdown();
var writer = new StringWriter();
ToPlainText(markdown, writer, pipeline, context);
return writer.ToString();
diff --git a/src/Markdig/MarkdownPipeline.cs b/src/Markdig/MarkdownPipeline.cs
index 726770bd9..6ab8c661d 100644
--- a/src/Markdig/MarkdownPipeline.cs
+++ b/src/Markdig/MarkdownPipeline.cs
@@ -4,6 +4,7 @@
using System;
using System.IO;
+using System.Text;
using Markdig.Helpers;
using Markdig.Parsers;
using Markdig.Renderers;
@@ -68,40 +69,76 @@ public void Setup(IMarkdownRenderer renderer)
}
- private HtmlRendererCache _rendererCache = null;
+ private HtmlRendererCache _rendererCache, _rendererCacheForCustomWriter;
- internal HtmlRenderer GetCacheableHtmlRenderer()
+ internal RentedHtmlRenderer RentHtmlRenderer(TextWriter writer = null)
{
- if (_rendererCache is null)
+ HtmlRendererCache cache = writer is null
+ ? _rendererCache ??= new HtmlRendererCache(this, customWriter: false)
+ : _rendererCacheForCustomWriter ??= new HtmlRendererCache(this, customWriter: true);
+
+ HtmlRenderer renderer = cache.Get();
+
+ if (writer is not null)
{
- _rendererCache = new HtmlRendererCache
- {
- OnNewInstanceCreated = Setup
- };
+ renderer.Writer = writer;
}
- return _rendererCache.Get();
- }
- internal void ReleaseCacheableHtmlRenderer(HtmlRenderer renderer)
- {
- _rendererCache.Release(renderer);
+
+ return new RentedHtmlRenderer(cache, renderer);
}
- private sealed class HtmlRendererCache : ObjectCache
+ internal sealed class HtmlRendererCache : ObjectCache
{
- public Action OnNewInstanceCreated;
+ private const int InitialCapacity = 1024;
+
+ private static readonly StringWriter _dummyWriter = new();
+
+ private readonly MarkdownPipeline _pipeline;
+ private readonly bool _customWriter;
+
+ public HtmlRendererCache(MarkdownPipeline pipeline, bool customWriter = false)
+ {
+ _pipeline = pipeline;
+ _customWriter = customWriter;
+ }
protected override HtmlRenderer NewInstance()
{
- var writer = new StringWriter();
+ var writer = _customWriter ? _dummyWriter : new StringWriter(new StringBuilder(InitialCapacity));
var renderer = new HtmlRenderer(writer);
- OnNewInstanceCreated(renderer);
+ _pipeline.Setup(renderer);
return renderer;
}
protected override void Reset(HtmlRenderer instance)
{
- instance.Reset();
+ instance.ResetInternal();
+
+ if (_customWriter)
+ {
+ instance.Writer = _dummyWriter;
+ }
+ else
+ {
+ ((StringWriter)instance.Writer).GetStringBuilder().Length = 0;
+ }
+
}
}
+
+ internal readonly struct RentedHtmlRenderer : IDisposable
+ {
+ private readonly HtmlRendererCache _cache;
+ public readonly HtmlRenderer Instance;
+
+ internal RentedHtmlRenderer(HtmlRendererCache cache, HtmlRenderer renderer)
+ {
+ _cache = cache;
+ Instance = renderer;
+ }
+
+ public void Dispose() => _cache.Release(Instance);
+ }
+
}
}
\ No newline at end of file
diff --git a/src/Markdig/Parsers/MarkdownParser.cs b/src/Markdig/Parsers/MarkdownParser.cs
index af7d68515..07c24c1b9 100644
--- a/src/Markdig/Parsers/MarkdownParser.cs
+++ b/src/Markdig/Parsers/MarkdownParser.cs
@@ -155,28 +155,19 @@ private string FixupZero(string text)
return text.Replace('\0', CharHelper.ReplacementChar);
}
- private sealed class ContainerItemCache : DefaultObjectCache
- {
- protected override void Reset(ContainerItem instance)
- {
- instance.Container = null;
- instance.Index = 0;
- }
- }
-
private void ProcessInlines()
{
// "stackless" processor
- var cache = new ContainerItemCache();
- var blocks = new Stack();
+ int blockCount = 1;
+ var blocks = new ContainerItem[4];
- // TODO: Use an ObjectCache for ContainerItem
- blocks.Push(new ContainerItem(document));
+ blocks[0] = new ContainerItem(document);
document.OnProcessInlinesBegin(inlineProcessor);
- while (blocks.Count > 0)
+
+ while (blockCount != 0)
{
process_new_block:
- var item = blocks.Peek();
+ ref ContainerItem item = ref blocks[blockCount - 1];
var container = item.Container;
for (; item.Index < container.Count; item.Index++)
@@ -212,35 +203,31 @@ private void ProcessInlines()
// Else we have processed it
item.Index++;
}
- var newItem = cache.Get();
- newItem.Container = (ContainerBlock)block;
- block.OnProcessInlinesBegin(inlineProcessor);
- newItem.Index = 0;
- ThrowHelper.CheckDepthLimit(blocks.Count);
- blocks.Push(newItem);
+
+ if (blockCount == blocks.Length)
+ {
+ Array.Resize(ref blocks, blockCount * 2);
+ ThrowHelper.CheckDepthLimit(blocks.Length);
+ }
+ blocks[blockCount++] = new ContainerItem(newContainer);
+ newContainer.OnProcessInlinesBegin(inlineProcessor);
goto process_new_block;
}
}
- item = blocks.Pop();
- container = item.Container;
container.OnProcessInlinesEnd(inlineProcessor);
-
- cache.Release(item);
+ blocks[--blockCount] = default;
}
}
- private class ContainerItem
+ private struct ContainerItem
{
- public ContainerItem()
- {
- }
-
public ContainerItem(ContainerBlock container)
{
Container = container;
+ Index = 0;
}
- public ContainerBlock Container;
+ public readonly ContainerBlock Container;
public int Index;
}
diff --git a/src/Markdig/Renderers/TextRendererBase.cs b/src/Markdig/Renderers/TextRendererBase.cs
index f689cb2d2..62f716321 100644
--- a/src/Markdig/Renderers/TextRendererBase.cs
+++ b/src/Markdig/Renderers/TextRendererBase.cs
@@ -28,9 +28,7 @@ public abstract class TextRendererBase : RendererBase
protected TextRendererBase(TextWriter writer)
{
if (writer == null) ThrowHelper.ArgumentNullException_writer();
- this.Writer = writer;
- // By default we output a newline with '\n' only even on Windows platforms
- Writer.NewLine = "\n";
+ Writer = writer;
}
///
@@ -47,6 +45,8 @@ public TextWriter Writer
ThrowHelper.ArgumentNullException(nameof(value));
}
+ // By default we output a newline with '\n' only even on Windows platforms
+ value.NewLine = "\n";
writer = value;
}
}
@@ -128,6 +128,11 @@ protected internal void Reset()
ThrowHelper.InvalidOperationException("Cannot reset this TextWriter instance");
}
+ ResetInternal();
+ }
+
+ internal void ResetInternal()
+ {
childrenDepth = 0;
previousWasLine = true;
indents.Clear();
diff --git a/src/Markdig/Syntax/MarkdownObject.cs b/src/Markdig/Syntax/MarkdownObject.cs
index 08fc9c24a..fd8d90db6 100644
--- a/src/Markdig/Syntax/MarkdownObject.cs
+++ b/src/Markdig/Syntax/MarkdownObject.cs
@@ -22,8 +22,7 @@ protected MarkdownObject()
/// as we expect less than 5~10 entries, usually typically 1 (HtmlAttributes)
/// so it will gives faster access than a Dictionary, and lower memory occupation
///
- private DataEntry[] attachedDatas;
- private int count;
+ private DataEntries _attachedDatas;
///
/// Gets or sets the text column this instance was declared (zero-based).
@@ -55,33 +54,7 @@ public string ToPositionText()
/// The key.
/// The value.
/// if key is null
- public void SetData(object key, object value)
- {
- if (key == null) ThrowHelper.ArgumentNullException_key();
- if (attachedDatas == null)
- {
- attachedDatas = new DataEntry[1];
- }
- else
- {
- for (int i = 0; i < count; i++)
- {
- if (attachedDatas[i].Key == key)
- {
- attachedDatas[i].Value = value;
- return;
- }
- }
- if (count == attachedDatas.Length)
- {
- var temp = new DataEntry[attachedDatas.Length + 1];
- Array.Copy(attachedDatas, 0, temp, 0, count);
- attachedDatas = temp;
- }
- }
- attachedDatas[count] = new DataEntry(key, value);
- count++;
- }
+ public void SetData(object key, object value) => (_attachedDatas ??= new DataEntries()).SetData(key, value);
///
/// Determines whether this instance contains the specified key data.
@@ -89,23 +62,7 @@ public void SetData(object key, object value)
/// The key.
/// true if a data with the key is stored
/// if key is null
- public bool ContainsData(object key)
- {
- if (key == null) ThrowHelper.ArgumentNullException_key();
- if (attachedDatas == null)
- {
- return false;
- }
-
- for (int i = 0; i < count; i++)
- {
- if (attachedDatas[i].Key == key)
- {
- return true;
- }
- }
- return false;
- }
+ public bool ContainsData(object key) => _attachedDatas?.ContainsData(key) ?? false;
///
/// Gets the associated data for the specified key.
@@ -113,22 +70,7 @@ public bool ContainsData(object key)
/// The key.
/// The associated data or null if none
/// if key is null
- public object GetData(object key)
- {
- if (key == null) ThrowHelper.ArgumentNullException_key();
- if (attachedDatas == null)
- {
- return null;
- }
- for (int i = 0; i < count; i++)
- {
- if (attachedDatas[i].Key == key)
- {
- return attachedDatas[i].Value;
- }
- }
- return null;
- }
+ public object GetData(object key) => _attachedDatas?.GetData(key);
///
/// Removes the associated data for the specified key.
@@ -136,44 +78,117 @@ public object GetData(object key)
/// The key.
/// true if the data was removed; false otherwise
///
- public bool RemoveData(object key)
+ public bool RemoveData(object key) => _attachedDatas?.RemoveData(key) ?? false;
+
+ private class DataEntries
{
- if (key == null) ThrowHelper.ArgumentNullException_key();
- if (attachedDatas == null)
+ private struct DataEntry
+ {
+ public readonly object Key;
+ public object Value;
+
+ public DataEntry(object key, object value)
+ {
+ Key = key;
+ Value = value;
+ }
+ }
+
+ private DataEntry[] _entries;
+ private int _count;
+
+ public DataEntries()
{
- return true;
+ _entries = new DataEntry[2];
}
- for (int i = 0; i < count; i++)
+ public void SetData(object key, object value)
{
- if (attachedDatas[i].Key == key)
+ if (key == null) ThrowHelper.ArgumentNullException_key();
+
+ DataEntry[] entries = _entries;
+ int count = _count;
+
+ for (int i = 0; i < entries.Length && i < count; i++)
{
- if (i < count - 1)
+ ref DataEntry entry = ref entries[i];
+ if (entry.Key == key)
{
- Array.Copy(attachedDatas, i + 1, attachedDatas, i, count - i - 1);
+ entry.Value = value;
+ return;
}
- count--;
- attachedDatas[count] = new DataEntry();
- return true;
}
+
+ if (count == entries.Length)
+ {
+ Array.Resize(ref _entries, count + 2);
+ }
+
+ _entries[count] = new DataEntry(key, value);
+ _count++;
}
- return false;
- }
- ///
- /// Store a Key/Value pair.
- ///
- private struct DataEntry
- {
- public DataEntry(object key, object value)
+ public object GetData(object key)
{
- Key = key;
- Value = value;
+ if (key == null) ThrowHelper.ArgumentNullException_key();
+
+ DataEntry[] entries = _entries;
+ int count = _count;
+
+ for (int i = 0; i < entries.Length && i < count; i++)
+ {
+ ref DataEntry entry = ref entries[i];
+ if (entry.Key == key)
+ {
+ return entry.Value;
+ }
+ }
+
+ return null;
}
- public readonly object Key;
+ public bool ContainsData(object key)
+ {
+ if (key == null) ThrowHelper.ArgumentNullException_key();
- public object Value;
+ DataEntry[] entries = _entries;
+ int count = _count;
+
+ for (int i = 0; i < entries.Length && i < count; i++)
+ {
+ if (entries[i].Key == key)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public bool RemoveData(object key)
+ {
+ if (key == null) ThrowHelper.ArgumentNullException_key();
+
+ DataEntry[] entries = _entries;
+ int count = _count;
+
+ for (int i = 0; i < entries.Length && i < count; i++)
+ {
+ if (entries[i].Key == key)
+ {
+ if (i < count - 1)
+ {
+ Array.Copy(entries, i + 1, entries, i, count - i - 1);
+ }
+ count--;
+ entries[count] = default;
+ _count = count;
+ return true;
+ }
+ }
+
+ return false;
+ }
}
}
}
\ No newline at end of file